<?php

namespace control;

/*
*销售发货单类
*/

class saleSendOrderClass extends adIndexClass
{
    private const page = 'saleSendOrder';
    private const table = 'sale_send_order';
    private const tableMx = 'sale_send_order_detail';
    private const title = '销售发货单';

    /**
     * 字段初始化
     */
    public function fieldInit($fun): array
    {
        switch ($fun) {
            case 'clientSendOrderSearch':
                $tr = [
                    'orderNo'    => [
                        'name'  => '单据编号',
                        'width' => 300,
                        'sort'  => 1,
                    ],
                    'manager'    => [
                        'name'  => "销售员",
                        'width' => 300,
                    ],
                    'money'      => [
                        'name'  => '发货金额(' . static::$unit . ')',
                        'width' => 300,
                        'sort'  => 1,
                    ],
                    'recordDate' => [
                        'name'  => '单据日期',
                        'width' => 300,
                        'sort'  => 1,
                    ],
                    'state'      => [
                        'name'  => '状态',
                        'width' => 300,
                    ]
                ];
                break;
            default:
                $tr = [
                    'orderNo'     => [
                        'name'  => '单据编号',
                        'width' => 200,
                        'sort'  => 1,
                    ],
                    'companyName' => [
                        'name'  => "客户",
                        'width' => 200,
                    ],
                    'tag'         => [
                        'name'  => "单据标签",
                        'width' => 300,
                    ],
                    'manager'     => [
                        'name'  => '销售员',
                        'width' => 200,
                    ],
//                    'money'       => [
//                        'name'  => '发货金额(' . static::$unit . ')',
//                        'width' => 200,
//                        'sort'  => 1,
//                    ],
                    'recordDate'  => [
                        'name'  => '单据日期',
                        'width' => 200,
                        'sort'  => 1,
                    ],
                    'state'       => [
                        'name'  => '状态',
                        'width' => 200,
                    ]
                ];
        }
        return $tr;
    }

    /*列表页-查询条件*/
    public function clientSendOrderOptions($isAsync = true)
    {
        //生成查询表单
        $html = $this->tableShow([
            [
                [
                    "单据编号" => text(array("name" => "orderNo")) . hidden(array("name" => "targetId", "value" => $this->get('id'))),
                    '销售员'  => selectMultiple(["name" => "stid", 'table' => " staff", 'sqlId' => 'stid', 'sqlField' => 'name']),
                    "单据日期" => text(array("name" => "createTime", 'laydate' => ['type' => 'date', 'range' => '~'])),
                ],
                [
                    "状态" => select(["name" => "state", 'option' => ['草稿', '待确认', '已确认']]),
                ]
            ]
        ], 1);
        //返回
        if (!$isAsync) {
            return $html;
        } else {
            return json_encode(['html' => $html], 320);
        }
    }

    /**
     * 客户页卡
     *
     * @return bool|string
     */
    public function clientSendOrder(): bool|string
    {
        $html   = $this->clientSendOrderOptions(false);
        $button = "";
        if ($this->power(self::page, "print")) {
            $button .= sprintf("<span onclick=\"batchPrint('%s', {targetId: '%s'})\" class='spanButton'>打印设置</span>\n", $this->get('cla'), $this->get('id'));
        }
        //返回
        $json['html'] = $this->search($html, layout: 'pop', toolButton: $button);
        return json_encode($json, 320);
    }

    public function clientSendOrderSearch(): bool|string
    {
        $khid = $this->post('targetId');
        //like查询数组
        $like = array(
            "orderNo" => $this->post('orderNo'),//证件编号
        );
        //全等查询数组
        $equal = array(
            "state"   => $this->post('state'),
            "manager" => $this->post('stid'),
            'coid'    => self::$coid,
        );
        $and   = " and khid = '$khid'";
        if ($this->post('createTime')) {
            $and .= self::timeComparison($this->post('createTime'), 'date', 'recordDate');
        }
        //排序
        $orderBy = $this->listSort(array(
            "search" => $this->post('list_sort_key') ?: 'recordDate desc,time',
            "sort"   => $this->post('list_sort') ?: "desc",
            "array"  => array(),
        ));
        //获取查询结果
        $where = search(array("like" => $like, "=" => $equal)) . $and;
        $page  = $this->page(array("table" => self::table, "where" => $where, "order" => $orderBy['orderBy']));
        //获取自定义字段参数
        $installSearch = $this->installSearch();
        $body          = [];
        //表格控件区
        $liButton = array("");
        //获取记录
        $url = root . "control/adIndex.html?cla=" . self::page;
        foreach ($page['array'] as $array) {
            //列表控件
            $dominate   = array(
                "box"    => array("name" => "listId", "value" => $array['id']),
                "detail" => array("title" => self::title . "详情", "click" => "newIframe('" . self::title . "详情','" . $url . "&fun=detail&id=" . $array['id'] . "')"),
            );
            $liButton[] = $this->dominate($dominate);
            //本行初始化
            $row = [];
            foreach ($installSearch['key'] as $key) {
                switch ($key) {
                    case "money":
                        $row[] = self::lostZero($array['money'], 'amount');
                        break;
                    case "manager":
                        $row[] = self::findStaff($array['manager']);
                        break;
                    default:
                        $row[] = isset($array[$key]) && $array[$key] ? $array[$key] : '';
                }
            }
            $body[] = $row;
        }
        //控件
        $button = "";
        //生成页面
        $li = $this->li(array(
            "body"        => $body,
            "install"     => $installSearch,
            "button"      => array("html" => $liButton),
            "orderBySign" => $orderBy,
            "page"        => $page['para'],
        ));
        //返回
        $json['html'] = $page['hint'] . $button . "<form name='sendForm'>" . $li . "</form>" . $page['button'] . clear;
        return json_encode($json, 320);
    }

    /*列表页-查询条件*/
    public function homeOptions($isAsync = true)
    {
        //生成查询表单
        $html = $this->tableShow([
            [
                [
                    "单据编号" => text(array("name" => "orderNo")),
                    "标签"   => text(array("name" => "tages")),
                    '客户名称' => clientProfileClass::kehuSelect(array('title' => '')),
                ],
                [
                    '销售员'  => selectMultiple(["name" => "stid", 'table' => " staff", 'sqlId' => 'stid', 'sqlField' => 'name']),
                    "单据日期" => text(array("name" => "createTime", 'laydate' => ['type' => 'date', 'range' => '~'])),
                    "状态"   => select(["name" => "state", 'option' => ['草稿', '待确认', '已确认']]),
                ]
            ]
        ], 1);
        //返回
        if (!$isAsync) {
            return $html;
        } else {
            return json_encode(['html' => $html], 320);
        }
    }

    /*列表-查询条件*/
    public function home()
    {
        $html = $this->homeOptions(false);
        //控件
        $button = "";
        $url    = root . "control/adIndex.html?cla=" . self::page;
        if ($this->power(self::page, "edit")) {
            $button .= "
            <span onclick=\"newIframe('" . self::title . "详情','" . $url . "&fun=edit')\" class='spanButton'>新增</span>\n";
        }
        if ($this->power(self::page, "del")) {
            $button .= "<span class='spanButton' onclick=\"layer('{$this->get('cla')}', 'operate', {'act': 'del'})\">删除</span>\n";
        }
        if ($this->power(self::page, "print")) {
            $button .= " <span onclick=\"batchPrint('" . $this->get('cla') . "')\" class='spanButton'>批量打印</span>\n";
        }
        if ($this->power(self::page, "edit")) {
            $button .= "
            <span onclick=\"layer('orderTag','batchAddTags',{'table':'" . self::table . "','title':'" . self::title . "'})\" class='spanButton'>批量打标签</span>\n
            ";
        }
        //返回
        return $this->homeFun($this->search($html, layout: 'pop', toolButton: $button));
    }

    /*客户列表-查询结果*/
    public function homeSearch()
    {
        //like查询数组
        $like = array(
            "orderNo" => $this->post('orderNo'),//证件编号
        );
        //全等查询数组
        $equal = array(
            "state" => $this->post('state'),
            "stid"  => $this->post('stid'),
            "khid"  => $this->post('khid'),
            'coid'  => self::$coid,
        );
        $and   = '';
        if ($this->post('createTime')) {
            $and .= self::timeComparison($this->post('createTime'), 'date', 'recordDate');
        }
        if ($this->post('tages')) {
            $and     .= " and (";
            $tagsArr = explode('，', $this->post('tages'));
            foreach ($tagsArr as $index => $tag) {
                $and .= $index == 0 ? "tags like '%" . $tag . "%'" : " and tags like '%" . $tag . "%'";
            }
            $and .= ")";
        }
        //排序
        $orderBy = $this->listSort(array(
            "search" => $this->post('list_sort_key') ?: 'recordDate desc,orderNo',
            "sort"   => $this->post('list_sort') ?: "desc",
            "array"  => array(),
        ));
        //获取查询结果
        $where = search(array("like" => $like, "=" => $equal)) . $and;
        $page  = $this->page(array("table" => self::table, "where" => $where, "order" => $orderBy['orderBy']));
        //获取自定义字段参数
        $installSearch = $this->installSearch();
        $body          = [];
        //表格控件区
        $liButton = array(
            $this->checkAll("listForm", "listId")
        );
        //获取记录
        $url = root . "control/adIndex.html?cla=" . self::page;
        foreach ($page['array'] as $array) {
            //列表控件
            $dominate = array(
                "box"    => array("name" => "listId", "value" => $array['id']),
                "edit"   => array("title" => "编辑", "click" => "newIframe('" . self::title . "详情','" . $url . "&fun=edit&id=" . $array['id'] . "')"),
                "detail" => array("title" => self::title . "详情", "click" => "newIframe('" . self::title . "详情','" . $url . "&fun=detail&id=" . $array['id'] . "')"),
                'print'  => array('title' => '打印', 'click' => "window.open('" . root . "control/adIndex.html?cla=" . self::page . "&fun=printPage&id={$array['id']}')"),
                'track'  => array("title" => "单据追踪", "click" => "newIframe('单据追踪','" . root . "control/adIndex.html?cla=orderTrack&fun=home&orderType=销售发货单&id=" . $array['id'] . "')"),
            );
            if ($array['state'] == '已确认') {
                unset($dominate['edit']);
            }
            $liButton[] = $this->dominate($dominate);
            //本行初始化
            $row = [];
            foreach ($installSearch['key'] as $key) {
                switch ($key) {
                    case "companyName":
                        $kehu  = query('kehu', "khid = '$array[khid]'");
                        $row[] = $kehu['companyName'];
                        break;
                    case "tag":
                        $tag   = orderTagClass::getTagName($array['id']);
                        $row[] = $tag;
                        break;
                    case "manager":
                        $row[] = self::findStaff($array['manager']);
                        break;
                    case "money":
                        $row[] = self::lostZero($array['money'], 'amount');
                        break;
                    default:
                        $row[] = isset($array[$key]) && $array[$key] ? $array[$key] : '';
                }
            }
            $body[] = $row;
        }
        //控件
        $button   = "";
        $numTotal = find(array(
            'table'  => self::table,
            'single' => $where,
            'field'  => "ifnull(sum(money),0) as money"
        ));
        $total    = [
            'money' => self::lostZero($numTotal['money']),
        ];
        //生成页面
        $li = $this->li(array(
            "body"        => $body,
            "install"     => $installSearch,
            "button"      => array("html" => $liButton),
            "orderBySign" => $orderBy,
            "page"        => $page['para'],
            "total"       => $total,
        ));
        //返回
        $json['html'] = $page['hint'] . $button . "<form name='listForm'>" . $li . "</form>" . $page['button'] . clear;
        return json_encode($json, 320);
    }

    /**
     * 保存并提交
     * @return bool|string
     */
    public function saveAndCommit(): bool|string
    {
        $resultJson = $this->editEdit();
        $result     = json_decode($resultJson, true);
        if ($result['warn'] == 2) {
            unset($_SESSION['warn']);
            $resultJson = $this->commitEdit();
        }
        return $resultJson;
    }

    /**
     * 编辑页
     *
     * @return string
     */
    public function edit(): string
    {
        $id   = $this->get('id') ?: suiji();
        $info = query(self::table, " id = '" . $id . "' ");

        $button[] = [
            "name"  => "保存",
            "type"  => "点击事件",
            "title" => "保存",
            'fun'   => "subForm('" . self::page . "EditForm','" . root . "control/adIndex.html?cla=" . self::page . "&fun=editEdit')",
        ];

//        $result   = find(array('table' => 'warehouse', 'where' => "coid = '" . self::$coid . "' and state = '启用' order by time desc"));
//        $li       = '';
//        if (is_array($result['array'])) {
//            foreach ($result['array'] as $array) {
//                $li .= "<li data-id='$array[id]'>{$array['name']}</li>";
//            }
//        }
        $tr[]     = [
            '',
            '产品编码',
            '产品名称',
            '规格型号',
            must.'单位',
            must.'发货数量',
            '变更数量',
            '重量（吨）',
            '100L钢瓶',
            '400L钢瓶',
            '800L钢瓶',
            '关联销售订单',
        ];
        $totalArr = ['total_num' => 0, 'total_row_money' => 0, 'total_tax_money' => 0, 'total_money' => 0];
        if (empty($info['id']) && $this->get('orderId')) {
            $sale_order = query('sale_order', "id = '" . $this->get('orderId') . "'", "id,orderNo,isTax,khid,deliveryDate");
            if (!$info['isTax']) {
                $info['isTax'] = $sale_order['isTax'];
            }
            $info['khid']      = $sale_order['khid'];
            $info['limitTime'] = $sale_order['deliveryDate'];
            //订单
            $res = find(array(
                'table' => "sale_order_detail",
                'where' => " orderId = '$sale_order[id]' order by list asc",
                'field' => "*"
            ));
            foreach ((array)$res['array'] as $k => $saleOrderMx) {
                //当前剩余未发货的数量
                $oddNum = $saleOrderMx['num'] - $saleOrderMx['send_num'] + $saleOrderMx['refund_num'];
                $num    = $oddNum > 0 ? self::formattingFigure($oddNum, 'numPrecision') : 0;
                //产品
                $material = query('material', "id = '$saleOrderMx[materialId]'");
                //仓库
                $warehouseOption = self::warehouseOption($material['id'], $saleOrderMx['unit']);
                $warehouseId     = $material['warehouse'] ?: array_key_first($warehouseOption);
                //当前可用库存
                $result         = $this->getInventory($warehouseId, $material['id'], $saleOrderMx['unit']);
                $result         = json_decode($result, true);
                $result['num']  = $result['num'] ?? 0;
                $saleOrderMx['price']        = self::formattingFigure($saleOrderMx['price'], 'pricePrecision');
                $moneyRes                    = saleOrderClass::isTax($num, $saleOrderMx['price'], $saleOrderMx['rate'], $info['isTax']);
                $money                       = $moneyRes['money'];
                $taxMoney                    = $moneyRes['taxMoney'];
                $moneyWithTax                = $moneyRes['moneyWithTax'];
                $suiji                       = self::getId();
                $tr[]                        = [
                    componentClass::tablePperation($k + 1, "delTableRow(this);calculateTotal()") .
                    hidden(array('name' => 'mxId[]', 'value' => $suiji)) .
                    hidden(array('name' => 'materialId[]', 'value' => $saleOrderMx['materialId'])) .
                    hidden(array('name' => 'sale_detail_id[]', 'value' => $saleOrderMx['id'])) .
                    hidden(array('name' => 'saleOrderId[]', 'value' => $saleOrderMx['orderId'])),
                    $material['code'],
                    $material['name'],
                    $material['model'],
                    select(array(
                        'name'   => 'unitDetail[]',
                        'data'   => ['unit' => $saleOrderMx['unit']],
                        'change' => 'unitChange',
                        'option' => machiningOrderClass::unitOption($material['id']),
                        'title'  => '单位',
                        'value'  => $saleOrderMx['unit']
                    )),
                    text(array('name' => 'num[]', 'type' => 'text', 'blur' => 'inventoryWarning();countMoney', 'other' => self::digitalFiltering('numPrecision'), 'value' => $num)),
                    $saleOrderMx['change_num'],
                    text(array('name' => 'weight[]', 'value' => $saleOrderMx['weight'])),
                    text(array('name' => 'one_hundred_l[]', 'value' => 0)),
                    text(array('name' => 'four_hundred_l[]', 'value' => 0)),
                    text(array('name' => 'eight_hundred_l[]', 'value' => 0)),
                    $sale_order['orderNo'],
                ];
                $totalArr['total_num']       += $num;
                $totalArr['total_row_money'] += $money;
                $totalArr['total_tax_money'] += $taxMoney;
                $totalArr['total_money']     += $moneyWithTax;
            }
        } else {
            $result = find(array('table' => self::tableMx, 'where' => "orderId = '$info[id]' order by list"));
            foreach ($result['array'] as $k => $array) {
                $material    = query('material', " id = '$array[materialId]'");
                $money       = round($array['money'] - $array['taxMoney'], 2);
                $saleOrderMx = query("sale_order_detail", "id = '$array[saleOrderMxId]'");
                $saleOrder   = query('sale_order', "id = '$saleOrderMx[orderId]'");
                //当前可用库存（已包含占用库存）
                $unitOption                  = machiningOrderClass::unitOption($array['materialId']);
                $tr[]                        = [
                    componentClass::tablePperation($k + 1, "delTableRow(this);calculateTotal()") .
                    hidden(array('name' => 'mxId[]', 'value' => $array['id'])) .
                    hidden(array('name' => 'materialId[]', 'value' => $array['materialId'])) .
                    hidden(array('name' => 'sale_detail_id[]', 'value' => $array['saleOrderMxId'])).
                    hidden(array('name' => 'saleOrderId[]', 'value' => $saleOrderMx['orderId'])),
                    $material['code'],
                    $material['name'],
                    $material['model'],
                    select(array(
                        'name'   => 'unitDetail[]',
                        'data'   => ['unit' => $array['unit']],
                        'change' => 'unitChange',
                        'option' => $unitOption,
                        'title'  => '单位',
                        'value'  => $array['unit']
                    )),
                    text(array('name' => 'num[]', 'type' => 'text', 'blur' => 'inventoryWarning();countMoney', 'other' => self::digitalFiltering('numPrecision'), 'value' => $array['num'] ? self::lostZero($array['num'], 'numPrecision') : '')),
                    $array['change_num'],
                    text(array('name' => 'weight[]', 'value' => $saleOrderMx['weight'])),
                    text(array('name' => 'one_hundred_l[]', 'value' => $saleOrderMx['one_hundred_l'])),
                    text(array('name' => 'four_hundred_l[]', 'value' => $saleOrderMx['four_hundred_l'])),
                    text(array('name' => 'eight_hundred_l[]', 'value' => $saleOrderMx['eight_hundred_l'])),
                    $saleOrder['orderNo'],
                ];
                $totalArr['total_num']       += $array['num'];
                $totalArr['total_row_money'] += $money;
                $totalArr['total_tax_money'] += $array['taxMoney'];
                $totalArr['total_money']     += $array['money'];
            }
        }
        $total     = [
            10 => "<span id='total_num'>" . self::lostZero($totalArr['total_num']) . "</span>",
            15 => "<span id='total_row_money'>" . self::lostZero($totalArr['total_row_money']) . "</span>",
            17 => "<span id='total_tax_money'>" . self::lostZero($totalArr['total_tax_money']) . "</span>",
            18 => "<span id='total_money'>" . self::lostZero($totalArr['total_money']) . "</span>",
        ];
        $list      = $this->listTable(array('tr' => $tr, 'end' => true, 'total' => $total));
        //选项控制
        $setting = optionSettingClass::getSetting('销售');
        $mxButton = <<<efo
                <span class="smallButton" onclick="chooseOrder()">关联销售单</span>
                
efo;
        $kehu            = query('kehu', "khid = '$info[khid]'");
        $kehu['credits'] = $kehu['credits'] ?: 0;
        $settlement      = array_merge(para('supplierSettlement', '，'), ['月结']);
        //表单
        $data['button'] = $this->tableShowButton($button);
        $body           = $this->edit4([
            '基本信息' => [
                [
                    must . "单据日期：" => text(["name" => "recordDate", 'class' => 'text', 'laydate' => [], "value" => $info['recordDate'] ?: $this->date]),
                    must . "客户："   => componentClass::selectText([
                            'name'      => 'khid',
                            'value'     => $info['khid'],
                            'title' => $kehu['companyName'],
                            'cla'       => 'saleOrder',
                            'fun'       => 'getClient',
                            'change'    => 'getContract',
                        ]) .
                        "<span class='del-row-button' onclick=\"newIframe('我的客户','" . root . "control/adIndex.html?cla=client&fun=edit')\" >新建</span>",
                    "信用额度"         => "<span id='credits'>{$kehu['credits']}/" . self::arrears($info['khid']) . "</span>",
                    "合同："          =>
                        "<div id='contract' class='full'>" .
                        selectMultiple([
                            "name"     => "contractId",
                            "value"    => $info['contractId'],
                            'table'    => "sales_contract",
                            'where'    => "clientId = '$info[khid]' and state = '已通过'",
                            'sqlId'    => 'id',
                            'sqlField' => 'name',
                            'title'    => '请选择'
                        ]) .
                        "</div>",
                ],
                [
                    must . "交货方式：" => radio(array('name' => 'delivery_method', 'checked' => $info['delivery_method'] ?: '1', 'value' => saleOrderClass::deliveryMethod)),
                    must . "送货时限：" => text(["name" => "limitTime", 'class' => 'text', 'laydate' => ['type' => 'datetime'], "value" => $info['limitTime']]),
                    must . "结算方式：" => radio(array(
                            'name'    => 'settlementType',
                            'change'  => 'settlementDayDefault',
                            'checked' => $info['settlementType'] ?: $settlement[0],
                            'value'   => $settlement
                        )) . "&nbsp;" .
                        text(array('name' => 'settlementDay', 'type' => 'number', 'value' => $info['settlementDay'] ?: '', 'title' => '结算周期')),
                ],
                [
                    "收货人："           => text(["name" => "contactName", 'class' => 'text', "value" => $info['contactName']]) .
                        "<span class='del-row-button m-l-5' onclick=\"layer('" . self::page . "','receiver',{contractId:$('[name=contractId]').val(),khid:$('[name=khid]').val()})\">选择</span>",
                    "收货电话："          => text(["name" => "contactTel", 'class' => 'text', "value" => $info['contactTel']]),
                    "收货地址："          => text(["name" => "address", 'class' => 'text', "value" => $info['address']]),
                    must . "实际交货日期：" => text(["name" => "actual_delivery_date", 'class' => 'text', 'laydate' => [], "value" => $info['actual_delivery_date'] ?: $this->date]),
                ],
                [
                    "驾驶员："         =>
                        "<div class='full'>" .
                        selectMultiple([
                            "name"     => "delivery_name",
                            "value"    => $info['delivery_name'],
                            'table'    => "staff",
                            'where'    => " 1=1 ",
                            'sqlId'    => 'stid',
                            'sqlField' => 'name',
                            'title'    => '请选择',
                        ]) .
                        "</div>",
                    "押运员："         =>
                        "<div class='full'>" .
                        selectMultiple([
                            "name"     => "delivery_manage",
                            "value"    => $info['delivery_manage'],
                            'table'    => "staff",
                            'where'    => " 1=1 ",
                            'sqlId'    => 'stid',
                            'sqlField' => 'name',
                            'title'    => '请选择',
                        ]) .
                        "</div>",
                    "选择车辆："         =>
                        "<div class='full'>" .
                        selectMultiple([
                            "name"     => "car_id",
                            "value"    => $info['car_id'],
                            'table'    => "logistics_car",
                            'where'    => " 1=1 and state='正常' and coid='".self::$coid."'",
                            "field"    => "id,CONCAT(shortName, '',carNo) AS car_name",
                            'sqlId'    => 'id',
                            'sqlField' => 'car_name',
                            'title'    => '请选择',
                        ]) .
                        "</div>",
                ],
                [
                    "销售类型" => select(["name" => "orderType", 'option' => ['普通销售','自提销售']]),
                    "销售部门" => selectMultiple([
                        "name"     => "departmentId",
                        "value"    => $info['departmentId'],
                        'table'    => "department",
                        'where'    => "pid = 'ae3dda4d06a05556' ",
                        'sqlId'    => 'id',
                        'sqlField' => 'name',
                        'title'    => '请选择'
                    ]),
                    "备注：" => textarea(["name" => "text", 'class' => 'text', "value" => $info['text']]),
                ],
            ]
        ]);
        $body           .= $this->editList([
            'id'     => 'materialTable',
            'title'  => '发货明细',
            'button' => $mxButton,
            'html'   => $list,
        ]);
        $body           .= $this->editBlock([
            '附件' => fileUploadClass::addFileComponent($id, '销售发货单', self::page, self::page . 'EditForm')
        ]);
        $data['html']   = $this->pageEdit([
            "html"   => $body,
            'hidden' => hidden(['name' => 'id', 'value' => $id]),
        ]);

        $saleOrderObj = new saleOrderClass();
        $cla          = self::page;
        $data['html'] .= <<<efo
<style>
.stateBlock{margin-top: 0}
.stateBlock ul li{padding: 2px 10px}
</style>
<script>
$(function() {
  inventoryWarning()
})
 //点击非下拉区域隐藏
$(document).bind('click', function (e) {
    let target = $(".stateBlock");
    let obj = $(".warehouse");
    if(!target.is(e.target) && target.has(e.target).length ===0 && !obj.is(e.target) && obj.has(e.target).length ===0){
      target.hide();
    }
});
$("#chooseWareHouse").on('click',function() {
   $(".stateBlock").show();
})
$(".warehouse .stateBlock li").on('click',function() {
    let id = $(this).data('id');
     $("select[name='warehouseId[]']").each(function() {
      let obj = $(this);
      obj.val(id);
      getInventory(this);
    });
   $(".stateBlock").hide();
})
function addRow(classify = ''){
    if (classify =='saleOrder'){
        $.post(root+'control/adIndex.html?cla={$cla}&fun=addOrderEdit',$("form[name='saleOrderForm']").serialize(),function(res) {
            $("#materialTable tbody").append(res.html);
            resetNumber($("#materialTable"))
            if (res.isTax){
                 $("[name='isTax']").prop('checked', true)
                 taxChange();
             }
            inventoryWarning()
            $("[name='saleOrderForm'] [name='saleOrderDetailId\[\]'").prop('checked', false);
        },'json');
    }else{
         $.post(root+'control/adIndex.html?cla={$cla}&fun=addMxEdit',$("form[name='mxForm']").serialize(),function(res) {
            $("#materialTable tbody").append(res.html);
            resetNumber($("#materialTable"))
            inventoryWarning()
            $("[name='mxForm'] [name='mxId\[\]'").prop('checked', false);
        },'json');
    }
    
}
//选择收货地址
function chooseRec(){
     $('#saleSendOrderReceiver').hide();
      $.post(root+'control/adIndex.html?cla={$cla}&fun=getReceiver',$("form[name='recForm']").serialize(),function(res) {
          $("[name='contactName']").val(res.data.name);
          $("[name='contactTel']").val(res.data.tel);
          $("[name='address']").val(res.data.address);
    },'json');
}
//根据客户获取合同
function getContract(){
    let khid = $("[name='khid']").val();
    $.post(root+'control/adIndex.html?cla={$cla}&fun=getContract',{khid:khid},function(res) {
          $("#contract").html(res.html);
          $("#credits").html(res.kehu.credits);
          $("[name='settlementDay']").val();
          $("[name='settlementType'][value='"+res.kehu.settleType+"']").attr('checked',true)
    },'json');
}
//获取当前可用库存
function getInventory(e){
    let warehouseId = e.value;
    let par = $(e).closest('tr')
    let materialId = par.find("[name='materialId[]']").val();
    let unit = par.find("[name='unitDetail[]']").val();
    $.post(root+'control/adIndex.html?cla={$cla}&fun=getInventory',{warehouseId:warehouseId,materialId:materialId,unit:unit},function(res) {
          par.find(".inventory").html(res.num);
          par.find(".position").html(res.position);
          par.find(".batch").html(res.batch);
          par.find(".batchInventory").html(res.batchInventory);
          inventoryWarning()
    },'json');
}
//根据仓库获取批次
function getBatch(data){
    let positionId = data.id;
    let par = data.clickId.closest('tr')
    let warehouseId = par.find("[name='warehouseId[]']").val();
    let materialId = par.find("[name='materialId[]']").val();
    let unit = par.find("[name='unitDetail[]']").val();
    $.post(root+'control/adIndex.html?cla={$cla}&fun=getBatch',{positionId:positionId,warehouseId:warehouseId,materialId:materialId,unit:unit},function(res) {
          par.find(".batch").html(res.batch);
          par.find(".batchInventory").html(res.batchInventory);
          inventoryWarning()
    },'json');
}
//获取批次库存
function getBatchInventory(data){
    let batch = data.id;
    let par = data.clickId.closest('tr')
    let materialId = par.find("[name='materialId[]']").val();
    let warehouseId = par.find("[name='warehouseId[]']").val();
    let positionId = par.find("[name='positionId[]']").val();
    let unit = par.find("[name='unitDetail[]']").val();
    $.post(root+'control/adIndex.html?cla={$cla}&fun=getBatchInventory',{warehouseId:warehouseId,positionId:positionId,materialId:materialId,unit:unit,batch:batch},function(res) {
          par.find(".batchInventory").html(res.inventory);
          inventoryWarning()
    },'json');
}
function chooseOrder() {
    let khid  = $("[name='khid']").val();
    if (!khid){
        msg('请先选择客户','error');
        return false
    }
    layer('{$this->get('cla')}','addOrder',$('form[name=saleSendOrderEditForm]').serialize())
}
/*库存不足提醒*/
function inventoryWarning() {
    $("[name='num[]']").each(function () {
        let num = $(this).val()*1
        let tr = $(this).closest("tr")
        let stock = tr.find(".batchInventory").text()*1
        tr.removeClass('warning')
        if(num > stock){
            tr.addClass('warning')
        }
    });
}
</script>

efo. $saleOrderObj->editJs();
        //返回
        return $this->tab($data);
    }

    //欠款
    public static function arrears($khid)
    {
        if (empty($khid)) {
            return 0;
        }
        return find(array('table' => "record as a join kehu as b on a.typeId = b.khid", "where" => "a.type = '客户' and a.typeId = '$khid'", 'sum' => "money")) ?: 0;
    }

    /**
     * 根据仓库获取批号
     */
    public static function getBatchArr($materialId, $warehouseId = ''): array
    {
        if (empty($warehouseId)) {
            return [];
        }
        $where        = " 1=1 and materialId = '$materialId' and warehouseId = '$warehouseId' and coid = '" . self::$coid . "' ";
        $fieldArr     = self::resolveField([
            'field' => 'batch, SUM(num) as num',
            'group' => 'batch',
        ]);
        $where        .= $fieldArr['group'];
        $outAllowZero = optionSettingClass::getSettingItem('库存', 'outAllowZero');//允许零库存出库
        $having       = "";
        if (!$outAllowZero) {
            $having = " having SUM(num) > 0 ";
        }
        $result = find([
            'table' => 'inventory_record',
            'field' => $fieldArr['field'],
            'where' => $where . $having . " order by SUM(num) desc",
        ]);
        $batch  = [];
        if (is_array($result['array'])) {
            foreach ((array)$result['array'] as $k => $array) {
                if ($array['batch'] === '') {
                    $batch[''] = '无（' . self::lostZero($array['num'], 'numPrecision') . '）';
                } else {
                    $batch[$array['batch']] = $array['batch'] . '（' . self::lostZero($array['num'], 'numPrecision') . '）';
                }
            }
        }
        return $batch;
    }

    /**
     * 获取仓库选择下拉（带库存数量）
     */
    public static function warehouseOption($materialId, $unit): array
    {
        $where  = " coid = '" . self::$coid . "' and state = '启用' " . self::getWarehouseSql() . " order by time desc ";
        $result = find([
            'table' => 'warehouse',
            'field' => 'id,name',
            'where' => $where,
        ]);
        $option = [];
        if (is_array($result['array'])) {
            foreach ((array)$result['array'] as $val) {
                $inventory          = machiningOrderClass::inventory($materialId, $unit, $val['id']);
                $option[$val['id']] = $val['name'] . "（" . $inventory . "）";
            }
        }
        return $option;
    }

    /**
     * Desc : 获取当前可用库存
     * User : you name
     * Date : 2023-10-07 17:25
     * @param string $warehouseId
     * @param string $materialId
     * @param string $unit
     * @return bool|string
     */
    public function getInventory($warehouseId = '', $materialId = '', $unit = ''): bool|string
    {
        $unit        = $unit ?: $this->post('unit');
        $warehouseId = $warehouseId ?: $this->post('warehouseId');
        $materialId  = $materialId ?: $this->post('materialId');
        $material    = query('material', "id = '$materialId'", 'unit,id');
        //当前可用库存(已包含占用库存)
        $inventoryNum   = machiningOrderClass::inventory($materialId, $unit, $warehouseId);
        $json['num']    = $inventoryNum;
        $inventoryPrice = inventoryBalanceClass::unitPrice($materialId);
        $json['money']  = self::lostZero($inventoryPrice * $inventoryNum);
        $positionId     = inventoryBalanceClass::matchLocation($warehouseId);
        //$batchOption    = toolOverhaulOutClass::getBatchOption($materialId, $warehouseId, $positionId);
        //库位
        $json['position'] = componentClass::selectText([
            'class'      => 'width150',
            'name'      => 'positionId[]',
            'value'     => $positionId,
            'title' => warehousePositionClass::getName($positionId),
            'cla'       => 'toolOverhaulOut',
            'fun'       => 'getPositionByStock',
            'change'       => 'getBatch',
            'param'    => "{'materialId':'".$material['id']."','unit':'".$unit."','warehouseId':'".$warehouseId."','zero':'0'}",
            'attach'    => "<label class='checkbox'><input name='zero' type='checkbox' value='1'><span></span>&nbsp;显示零/负库存</label>",
        ]);
        //批号
        $json['batch']          = componentClass::selectText([
            'class'      => 'width150',
            'name'      => 'batch[]',
            'value'     => '',
            'title' => '',
            'cla'       => 'toolOverhaulOut',
            'fun'       => 'getBatchByStock',
            'change'       => 'getBatchInventory',
            'param'    => "{'materialId':'".$material['id']."','unit':'".$unit."','warehouseId':'".$warehouseId."','positionId':'".$positionId."'}",
        ]);
        $json['batchInventory'] = '';
        return json_encode($json, 320);
    }

    /** 根据库位获取批次
     * @param string $warehouseId
     * @param string $positionId
     * @param string $materialId
     * @param string $unit
     * @return mixed
     */
    public function getBatch($warehouseId = '', $positionId = '', $materialId = '', $unit = ''): mixed
    {
        $unit        = $unit ?: $this->post('unit');
        $warehouseId = $warehouseId ?: $this->post('warehouseId');
        $positionId  = $positionId ?: $this->post('positionId');
        $materialId  = $materialId ?: $this->post('materialId');
        $material    = query('material', "id = '$materialId'", 'unit,id');
        //$batchOption = toolOverhaulOutClass::getBatchOption($materialId, $warehouseId, $positionId);
        //批号
        $json['batch']          = componentClass::selectText([
            'class'      => 'width150',
            'name'      => 'batch[]',
            'value'     => '',
            'title' => '',
            'cla'       => 'toolOverhaulOut',
            'fun'       => 'getBatchByStock',
            'change'       => 'getBatchInventory',
            'param'    => "{'materialId':'".$material['id']."','unit':'".$unit."','warehouseId':'".$warehouseId."','positionId':'".$positionId."'}",
        ]);
        $json['batchInventory'] = 0;
        return json_encode($json, 320);
    }

    /**
     * 获取批次库存
     */
    public function getBatchInventory($warehouseId = '', $materialId = '', $unit = '', $batch = ''): bool|string
    {
        $unit        = $unit ?: $this->post('unit');
        $warehouseId = $warehouseId ?: $this->post('warehouseId');
        $positionId = $this->post('positionId')?:'';
        $materialId  = $materialId ?: $this->post('materialId');
        $batch       = $batch ?: $this->post('batch');
        $material    = query('material', "id = '$materialId'", 'unit');
        //当前批次库存
        $batchInventory    = inventoryBalanceClass::inventoryNum($materialId, $warehouseId, $positionId, batch: $batch);
        //$batchInventory    = machiningOrderClass::material_num_change($materialId, $material['unit'], $unit, $batchInventory);
        $json['inventory'] = $batchInventory;
        return json_encode($json, 320);
    }

    /**
     * 获取合同
     */
    public function getContract(): bool|string
    {
        $khid                       = $this->post('khid');
        $kehu                       = query('kehu', "khid = '$khid'");
        $json['kehu']['settleType'] = $kehu['settleType'];
        $json['kehu']['credits']    = ($kehu['credits'] ?: 0) . '/' . self::arrears($khid);
        $json['html']               = selectMultiple([
            "name"     => "contractId",
            'table'    => "sales_contract",
            'where'    => "clientId = '$khid' and state = '已通过' order by time desc",
            'sqlId'    => 'id',
            'sqlField' => 'name',
            'title'    => '请选择'
        ]);
        return json_encode($json, 320);
    }

    public function editEdit()
    {
        $fileIdArr           = $this->post('fileId');
        $id                  = $this->post('id') ?: self::getId();
        $mxIdArr             = $this->post('mxId') ?: [];
        $materialIdArr       = $this->post('materialId');
        $sale_detail_idArr   = $this->post('sale_detail_id');
        $saleOrderIdArr      = $this->post('saleOrderId');
        $numArr              = $this->post('num');
        $weightArr           = $this->post('weight');
        $one_hundred_lArr    = $this->post('one_hundred_l');
        $four_hundred_lArr   = $this->post('four_hundred_l');
        $eight_hundred_lArr  = $this->post('eight_hundred_l');
        $unitArr             = $this->post('unitDetail');
        $field               = [
            'text'                 => $this->post('text'),
            'contractId'           => $this->post('contractId'),
            'khid'                 => $this->post('khid'),
            'recordDate'           => $this->post('recordDate'),
            'limitTime'            => $this->post('limitTime'),
            'contactName'          => $this->post('contactName'),
            'contactTel'           => $this->post('contactTel'),
            'address'              => $this->post('address'),
            'isTax'                => $this->post('isTax') ? 1 : 0,
            'settlementType'       => $this->post('settlementType'),
            'settlementDay'        => $this->post('settlementDay'),
            'delivery_method'      => $this->post('delivery_method'),
            'actual_delivery_date' => $this->post('actual_delivery_date'),
            'departmentId' => $this->post('departmentId'),
            'orderType' => $this->post('orderType'),
            'delivery_name' => $this->post('delivery_name'),
            'car_id' => $this->post('car_id'),
            'delivery_manage' => $this->post('delivery_manage'),
        ];

        $where = " id = '$id' ";
        $info  = query(self::table, $where);
        //判断
        if (!$this->power(self::page, "edit")) {
            $json['warn'] = "你没有编辑" . self::title . "的权限";
        } elseif (!empty($info['id']) && $info['state'] == '已确认') {
            $json['warn'] = '此' . self::title . '已确认，不能修改';
        } else {
            $json['warn'] = '';
            //选项控制
            $setting = optionSettingClass::getSetting('销售');
            if (is_array($mxIdArr) && count($mxIdArr) > 0) {
                $dataIntegration = self::dataArrangement($sale_detail_idArr, $numArr);  //相同订单物料数量汇总处理（便于判读）
                //判断库存是否可发货和销售发货的数量是否超过的销售单+上浮的数量
                foreach ($mxIdArr as $index => $mxId) {
                    if (isset($sale_detail_idArr[$index]) && static::total(array('table' => 'sale_order_detail', 'where' => "id = '$sale_detail_idArr[$index]'")) > 0) {
                        //此销售单总数量
                        $saleOrderMx = query('sale_order_detail', "id = '$sale_detail_idArr[$index]'");
                        //已进入发货的销售单数量
                        $num      = $saleOrderMx['send_num'] - $saleOrderMx['refund_num'];
                        $orderNum = machiningOrderClass::material_num_change($saleOrderMx['materialId'], $unitArr[$index], $saleOrderMx['unit'], $dataIntegration[$sale_detail_idArr[$index]]);

                        $material = query('material', "id = '$saleOrderMx[materialId]'");
                        if (isset($setting['saleStrict']) && $setting['saleStrict']) {
                            $material['upFloat'] = 0;
                        }
                        if (self::formattingFigure($orderNum + $num, 'numPrecision') > self::formattingFigure($saleOrderMx['num'] * (1 + $material['upFloat']), 'numPrecision')) {
                            $json['warn'] = "产品【{$material['name']}】的数量已超出销售订单总数量";
                            break;
                        }
                    } elseif (isset($sale_detail_idArr[$index]) && static::total(array('table' => 'sales_contract_detail', 'where' => "id = '$sale_detail_idArr[$index]'")) > 0) {
                        //销售合同明细
                        $targetInfo = query('sales_contract_detail', "id = '$sale_detail_idArr[$index]'");
                        $material   = query('material', "id = '$targetInfo[materialId]'");
                        //已进入销售单的合同明细数量
                        $num      = $this->contractInSendNum($id, $sale_detail_idArr[$index], $material['unit']);
                        $orderNum = machiningOrderClass::material_num_change($targetInfo['materialId'], $unitArr[$index], $material['unit'], $dataIntegration[$sale_detail_idArr[$index]]);
                        if (self::formattingFigure($num + $orderNum, 'numPrecision') > $targetInfo['num']) {
                            $json['warn'] = "产品【{$material['name']}】的数量已超出销售合同明细中数量";
                            break;
                        }
                    }
                }
            }
            if (empty($json['warn'])) {
                $kehu = query('kehu', "khid = '$field[khid]'");
                $this->startTrans();
                $isPass           = true;//事务通过标志
                $field['manager'] = $kehu['manager'];
                if (!$info['id']) {
                    $userNo       = codeTemplateClass::generateCode(self::page, [
                        'prefix' => 'XSFH' . date('Ymd', strtotime($field['recordDate'])),
                        'table'  => self::table,
                        'field'  => 'orderNo',
                        'where'  => " and coid = '" . self::$coid . "'",
                    ], $field);
                    $field        = array_merge($field, [
                        'id'      => $id,
                        'orderNo' => $userNo,
                        'state'   => '草稿',
                        'coid'    => self::$coid,
                        'stid'    => $this->control['stid'],
                    ]);
                    $json['warn'] = static::insert(["table" => self::table, "field" => $field]);
                } else {
                    $json['warn'] = static::update([
                        "table" => self::table,
                        "field" => $field,
                        "where" => $where,
                    ]);
                }

                //添加或更新报价单明细
                if (is_array($mxIdArr)) {
                    foreach ($mxIdArr as $k => $nid) {
                        $mx                  = query(self::tableMx, "id = '$nid'");
                        $numArr[$k]          = self::formattingFigure($numArr[$k], 'numPrecision');
                        $tableName = 'sale_order_detail';
                        $sale_order_detail = query('sale_order_detail',"id='$sale_detail_idArr[$k]'");
                        $result            = saleOrderClass::isTax($numArr[$k], $sale_order_detail['price'], 13);
                        if (!empty($mx['id'])) {
                            //更新
                            $warn = static::update([
                                "table" => self::tableMx,
                                "field" => [
                                    'saleOrderMxId'    => $sale_detail_idArr[$k],
                                    'saleOrderId'      => $saleOrderIdArr[$k],
                                    'sourceTable'      => $tableName,
                                    'materialId'       => $materialIdArr[$k],
                                    'unit'             => $unitArr[$k],
                                    'num'              => $numArr[$k],
                                    'packing_quantity' => '',
                                    'box_capacity'     => '',
                                    'remainder'        => '',
                                    'batch'            => '',
                                    'price'            => $sale_order_detail['price'],
                                    'taxMoney'         => $result['taxMoney'],//税额
                                    'moneyWithOutTax'  => $result['moneyWithTax'],//不含税金额
                                    'money'            => $result['money'],//价税合计
                                    'rate'             => 13,
                                    'warehouseId'      => '',
                                    'positionId'       => '',
                                    'khMaterialCode'   => '',
                                    'khMaterialName'   => '',
                                    'text'   => '',
                                    'weight'      => $weightArr[$k],
                                    'one_hundred_l'       => $one_hundred_lArr[$k],
                                    'four_hundred_l'   => $four_hundred_lArr[$k],
                                    'eight_hundred_l'   => $eight_hundred_lArr[$k],
                                ],
                                "where" => " id = '$nid'",
                            ]);
                        } else {
                            //新增
                            $warn = static::insert([
                                "table" => self::tableMx,
                                "field" => [
                                    'id'               => $nid,
                                    'orderId'          => $id,
                                    'coid'             => self::$coid,
                                    'sourceTable'      => $tableName,
                                    'saleOrderMxId'    => $sale_detail_idArr[$k],
                                    'saleOrderId'      => $saleOrderIdArr[$k],
                                    'materialId'       => $materialIdArr[$k],
                                    'unit'             => $unitArr[$k],
                                    'num'              => $numArr[$k],
                                    'packing_quantity' => '',
                                    'box_capacity'     => '',
                                    'remainder'        => '',
                                    'batch'            => '',
                                    'price'            => 0,
                                    'taxMoney'         => 0,//税额
                                    'moneyWithOutTax'  => 0,//不含税金额
                                    'money'            => 0,//价税合计
                                    'rate'             => 0,
                                    'warehouseId'      => '',
                                    'positionId'       => '',
                                    'khMaterialCode'   => '',
                                    'khMaterialName'   => '',
                                    'text'   => '',
                                    'weight'      => $weightArr[$k],
                                    'one_hundred_l'       => $one_hundred_lArr[$k],
                                    'four_hundred_l'   => $four_hundred_lArr[$k],
                                    'eight_hundred_l'   => $eight_hundred_lArr[$k],
                                ],
                            ]);
                        }
                        if (!in_array($warn, ['新增成功', '更新成功'])) {
                            $isPass       = false;
                            $json['warn'] = $warn;
                            break;
                        }
                    }
                }
                //添加主表ID到附件表
                if (is_array($fileIdArr)) {
                    $warn = update(array(
                        'table' => 'file',
                        'field' => ['targetId' => $id],
                        'where' => " targetId = '' and id in ('" . implode("','", $fileIdArr) . "')"
                    ));
                    if ($warn != '更新成功') {
                        $json['warn'] = $warn;
                        $isPass       = false;
                    }
                }
                if (in_array($json['warn'], ["新增成功", "更新成功"]) && $isPass) {
                    $this->commitTrans();
                    delete(array('table' => self::tableMx, 'where' => " id not in ('" . implode("','", $mxIdArr) . "') and orderId = '$id'"));
                    $money = find(array('table' => self::tableMx, 'single' => "orderId = '$id'", 'field' => "ifnull(sum(money),0) as total"))['total'];
                    update(array('table' => self::table, 'field' => ['money' => self::formattingFigure($money, 'amount')], 'where' => "id = '$id'"));
                    if ($json['warn'] == '新增成功') {
                        self::insertOperateLog($id, self::title, "新建");
                    }
                    //添加日志
                    $type = mb_substr($json['warn'], 0, 2, 'utf-8');
                    $text = $this->control['name'] . $type . "了" . self::title . " id：【" . $id . '】';
                    $this->logText(["type" => self::page, "text" => $text]);
                    //返回
                    $_SESSION['warn'] = $json['warn'];
                    $json['warn']     = 2;
                    $json['href']     = root . "control/adIndex.html?cla=" . $this->get('cla') . "&fun=detail&id=" . $id;
                } else {
                    $this->rollback();
                }
            }
        }
        resp:
        return json_encode($json, 320);
    }


    /**
     * 获取收货人
     */
    public function getReceiver(): bool|string
    {
        $info = query('kehu_staff', "id = '" . $this->post('peopleId') . "'");
        $json['data'] = $info;
        return json_encode($json, 320);
    }

    /**
     * 选择收货人
     *
     * @return bool|string
     */
    public function receiver()
    {
        $html   =
            text(array("name" => "contactName", "title" => "收货人")) .
            text(array("name" => "contactTel", "title" => "收货电话")) .
            text(array("name" => "address", "title" => "收货地址")) .
            hidden(array('name' => 'contractId', 'value' => $this->post('contractId'))) .
            hidden(array('name' => 'khid', 'value' => $this->post('khid')));
        $button = "<input style='margin-left: 5px' onclick=\"chooseRec()\" type='button' class='searchSub' value='提交'>";
        $html   = $this->search($html);
        return $this->layer(array("title" => "选择收货人", "height" => 600, "width" => 1000, "html" => $html, "button" => $button));
    }

    /**
     * 选择收货人
     *
     * @return bool|string
     */
    public function receiverSearch()
    {
        //客户联系人
        $like  = [
            'name'    => $this->post('materialName'),
            'tel'     => $this->post('contactTel'),
            'address' => $this->post('address'),
        ];
        $equal = [
        ];
        $and   = " and khid = '" . $this->post('khid') . "' ";
        $where = search(["like" => $like, "=" => $equal]) . $and;
        $page  = $this->page([
            "table"   => "kehu_staff",
            "where"   => $where,
            'pageNum' => 50,
            "order"   => "`time` ASC",
        ]);
        //获取表头
        $tr[] = ['', '收货人', '收货电话', '收货地址'];
        //获取记录
        foreach ($page['array'] as $array) {
            $tr[] = array(
                "<input name='peopleId' value='$array[id]' type='radio'>",
                $array['name'],
                $array['tel'],
                $array['address'],
            );
        }
        $json['html'] = $page['hint'] .
            "<form name='recForm'>" .
            hidden(array('name' => 'contractId', 'value' => $this->post('contractId'))) .
            $this->tableMany(array("tr" => $tr, "showSort" => true, "page" => $page['para'])) .
            "</form>" .
            $page['button'];
        return json_encode($json, 320);
    }

    /**
     * 关联销售单
     *
     * @return bool|string
     */
    public function addOrder(): bool|string
    {
        $layerId = $this->get('cla') . ucwords($this->get('fun'));
        $html    =
            text(array("name" => "orderNo", "title" => "销售订单号")) .
            text(array("name" => "recordDate", 'class' => 'width200', 'laydate' => ['type' => 'date', 'range' => '~'], "title" => "单据日期")) .
            text(array("name" => "createTime", 'class' => 'width200', 'laydate' => ['type' => 'date', 'range' => '~'], "title" => "交货日期")) .
            text(array("name" => "materialName", "title" => "产品名称")) .
            text(array("name" => "model", "title" => "规格型号")) .
            select(["name" => "pending", "class" => "width100", 'option' => [1=>'全部', 2=>'大于零', 3=>'等于零', 4=>'小于零'], 'title'=>'待发货', 'value'=>2]).
            hidden(array('name' => 'khid', 'value' => $this->post('khid'))) .
            hidden(array('name' => 'id', 'value' => $this->post('id'))) .
            hidden(array('name' => 'isTax', 'value' => $this->post('isTax'))) .
            hidden(array('name' => 'sale_detail_id', 'value' => $this->post('sale_detail_id') ? implode(',', $this->post('sale_detail_id')) : ''));
        $button  = "<input onclick=\"addRow('saleOrder');$('#" . $layerId . "').remove()\" type='button' class='searchSub m-l-5' value='提交'>";
        $html    = $this->search($html);
        return $this->layer(array("title" => "选择产品", "height" => 600, "width" => 1200, "html" => $html, "button" => $button));
    }

    /**
     * 关联销售单
     *
     * @return bool|string
     */
    public function addOrderSearch(): bool|string
    {
        //$sale_detail_id = $this->post('sale_detail_id') ? explode(',', $this->post('sale_detail_id')) : [];//. "' and mx.id not in ('" . implode("','", $sale_detail_id) . "')";
        $where = " so.coid='" . self::$coid . "' and so.khid = '" . $this->post('khid') . "'";
        $and   = " and so.isClose = 0";
        $like  = [
            'so.orderNo' => $this->post('orderNo'),
            'mat.code'   => $this->post('code'),
            'mat.name'   => $this->post('materialName'),
            'mat.model'  => $this->post('model'),
        ];
        $equal = [
            'so.type'  => '销货',
            'so.state' => '已确认',
        ];
        if ($this->post('createTime')) {
            //交货日期
            $and .= self::timeComparison($this->post('createTime'), 'date', 'so.deliveryDate');
        }
        if ($this->post('recordDate')) {
            //单据日期
            $and .= self::timeComparison($this->post('recordDate'), 'date', 'so.recordDate');
        }
        if ($this->post('pending')) {
            $and .= match ($this->post('pending')) {
                '2' => " and (mx.num - mx.send_num) > 0 ",
                '3' => " and (mx.num - mx.send_num) = 0 ",
                '4' => " and (mx.num - mx.send_num) < 0 ",
                default => ""
            };
        }
        $where .= search(["like" => $like, "=" => $equal]) . $and;
        $page  = $this->page([
            "table"   => "sale_order_detail as mx join sale_order as so on mx.orderId = so.id join material as mat on mx.materialId = mat.id ",
            "field"   => "mx.id,mx.num,mx.send_num,so.orderNo,so.recordDate,so.deliveryDate,mat.code,mat.name,mat.model",
            "where"   => $where,
            'pageNum' => 50,
            "order"   => "so.recordDate desc,so.`time` desc",
        ]);
        //获取表头
        $checkAll = $this->checkAll("saleOrderForm", "saleOrderDetailId");
        $tr[]     = [$checkAll, '销售订单号', '单据日期', '交货日期', '产品编码', '产品名称', '规格型号', '订货数量', '已发货', '待发货'];
        //获取记录
        foreach ($page['array'] as $array) {
            $dominate = array(
                "box" => array("name" => "saleOrderDetailId", "value" => $array['id']),
            );
            $tr[]     = array(
                $this->dominate($dominate),
                $array['orderNo'],
                $array['recordDate'],
                $array['deliveryDate'],
                $array['code'],
                $array['name'],
                $array['model'],
                self::lostZero($array['num'], 'numPrecision'),
                self::lostZero($array['send_num'], 'numPrecision'),
                self::lostZero($array['num'] - $array['send_num'], 'numPrecision')
            );
        }
        $json['html'] = $page['hint'] .
            "<form name='saleOrderForm'>" .
            hidden(array('name' => 'id', 'value' => $this->post('id'))) .
            hidden(array('name' => 'isTax', 'value' => $this->post('isTax'))) .
            $this->tableMany(array("tr" => $tr, "showSort" => true, "page" => $page['para'])) .
            "</form>" .
            $page['button'];
        return json_encode($json, 320);
    }

    /**
     * 销售单已进入发货单中的数量
     */
    private function inSendOrderNum($sendOrderId, $saleOrderMxId, $toUnit): float|int
    {
        $orderNum = 0;
        $all      = find(array('table' => 'sale_send_order_detail', 'where' => " orderId !='$sendOrderId' and saleOrderMxId = '$saleOrderMxId' and coid = '" . self::$coid . "'"));
        foreach ($all['array'] as $array) {
            $orderNum += machiningOrderClass::material_num_change($array['materialId'], $array['unit'], $toUnit, $array['num']);
        }
        //销售退货单
        $all = find(array(
            'table' => 'sale_refund_order_detail as a join sale_refund_order as b on a.orderId = b.id',
            'field' => "a.*",
            'where' => " a.sourceId = '$saleOrderMxId' and a.coid = '" . self::$coid . "' and b.state = '已确认'"
        ));
        foreach ($all['array'] as $array) {
            $orderNum -= machiningOrderClass::material_num_change($array['materialId'], $array['unit'], $toUnit, $array['num']);
        }
        return $orderNum;
    }


    /**
     * 关联销售端
     */
    public function addOrderEdit()
    {
        $id      = $this->post('id');
        $isTax   = $this->post('isTax');
        $mxIdArr = $this->post('saleOrderDetailId');
        if (!$this->power(self::page, 'edit')) {
            $json['warn'] = '你还没有编辑权限';
        } elseif (empty($mxIdArr) || !is_array($mxIdArr)) {
            $json['warn'] = '请选择销售单';
        } else {
            $tr         = [];
            $isOrderTax = 0;
            foreach ($mxIdArr as $saleOrderDetailId) {
                //订单
                $saleOrderMx = find(array(
                    'table'  => "sale_order as a join sale_order_detail as b on a.id = b.orderId",
                    'single' => " b.id = '$saleOrderDetailId' order by list asc",
                    'field'  => "b.*,a.orderNo,a.isTax"
                ));
                if (!$isOrderTax && $saleOrderMx['isTax']) {
                    $isOrderTax = $saleOrderMx['isTax'];
                }
                //产品
                $material = query('material', "id = '$saleOrderMx[materialId]'");
                //仓库
                $warehouseOption = self::warehouseOption($material['id'], $saleOrderMx['unit']);
                $warehouseId     = $material['warehouse'] ?: array_key_first($warehouseOption);
                //库位
                $positionId = inventoryBalanceClass::matchLocation($warehouseId);
                //当前可用库存
                $result         = $this->getInventory($warehouseId, $material['id'], $saleOrderMx['unit']);
                $result         = json_decode($result, true);
                $result['num']  = $result['num'] ?? 0;

                //当前剩余未发货的数量
                $oddNum               = $saleOrderMx['num'] - $saleOrderMx['send_num'] + $saleOrderMx['refund_num'];
                $num                  = $oddNum > 0 ? self::formattingFigure($oddNum, 'numPrecision') : 0;
                $saleOrderMx['price'] = self::formattingFigure($saleOrderMx['price'], 'pricePrecision');
                $moneyRes             = saleOrderClass::isTax($num, $saleOrderMx['price'], $saleOrderMx['rate'], $isTax);

                $suiji                = self::getId();
                $tr[]                 = [
                    componentClass::tablePperation('', "delTableRow(this);calculateTotal()") .
                    hidden(array('name' => 'mxId[]', 'value' => $suiji)) .
                    hidden(array('name' => 'materialId[]', 'value' => $saleOrderMx['materialId'])) .
                    hidden(array('name' => 'sale_detail_id[]', 'value' => $saleOrderMx['id'])).
                    hidden(array('name' => 'saleOrderId[]', 'value' => $saleOrderMx['orderId'])),
                    $material['code'],
                    $material['name'],
                    $material['model'],
                    select(array(
                        'name'   => 'unitDetail[]',
                        'data'   => ['unit' => $saleOrderMx['unit']],
                        'change' => 'unitChange',
                        'option' => machiningOrderClass::unitOption($material['id']),
                        'title'  => '单位',
                        'value'  => $saleOrderMx['unit']
                    )),
                    text(array('name' => 'num[]', 'type' => 'text', 'blur' => 'inventoryWarning();countMoney', 'other' => self::digitalFiltering('numPrecision'), 'value' => $num)),
                    text(array('name' => 'packing_quantity[]', 'type' => 'text', 'other' => self::digitalFiltering('numPrecision'), 'value' => '')),
                    text(array('name' => 'weight[]', 'value' => 0)),
                    text(array('name' => 'one_hundred_l[]', 'value' => 0)),
                    text(array('name' => 'four_hundred_l[]', 'value' => 0)),
                    text(array('name' => 'eight_hundred_l[]', 'value' => 0)),
                    $saleOrderMx['orderNo'],

                ];
            }
            $json['isTax'] = $isOrderTax;
            $json['warn']  = 2;
            $json['html']  = $this->tableMany(['tr' => $tr, 'thead' => false]);
        }
        return json_encode($json, 320);
    }

    /**
     * @return bool|string
     */
    public function addMx()
    {
        $layerId  = $this->get('cla') . ucwords($this->get('fun'));
        $formName = 'mxForm';
        $html     =
            materialClass::typeSelect(name: 'type') .
            text(array("name" => "code", "title" => "产品编码")) .
            text(array("name" => "materialName", "title" => "产品名称")) .
            text(array("name" => "model", "title" => "规格型号")) .
            hidden(array('name' => 'contractId', 'value' => $this->post('contractId'))) .
            hidden(array('name' => 'isTax', 'value' => $this->post('isTax'))) .
            hidden(array('name' => 'khid', 'value' => $this->post('khid'))) .
            hidden(array('name' => 'id', 'value' => $this->post('id'))) .
            hidden(array('name' => 'materialId', 'value' => $this->post('materialId') ? implode(',', $this->post('materialId')) : ''));
        $button   = "<input style='margin-left: 5px' onclick=\"addRow()\" type='button' class='searchSub' value='继续添加'>";
        $button   .= "<input style='margin-left: 5px' onclick=\"addRow();$('#" . $layerId . "').remove()\" type='button' class='searchSub' value='提交'>";
        $html     = $this->search($html);
        return $this->layer(array("title" => "添加", "height" => 600, "width" => 1000, "html" => $html, "button" => $button));
    }

    /**
     *添加产品
     */
    public function addMxSearch()
    {
        $contractId = $this->post('contractId');
        /*$materialId = $this->post('materialId') ? explode(',', $this->post('materialId')) : [];
        $where = " b.id not in ('" . implode("','", $materialId) . "')";*/
        $where = " 1=1 ";
        if ($contractId) {
            $where .= " and a.id in (select materialId from sales_contract_detail where orderId = '$contractId' and materialId!='')";
        } else {
            $where .= " and a.coid='" . self::$coid . "' ";
        }
        $like        = [
            'a.name'          => $this->post('materialName'),
            'code'            => $this->post('code'),
            'model'           => $this->post('model'),
            'a.typePath'      => $this->post('type'),
            'a.supplyControl' => saleOrderClass::ctrlSale() ? '允许销售' : '',
        ];
        $equal       = [
            'a.state' => '启用',
        ];
        $where       .= search(["like" => $like, "=" => $equal]);
        $page        = $this->page([
            "table"   => "material as a",
            "field"   => "a.*",
            "where"   => $where,
            'pageNum' => 50,
            "order"   => "a.`time` ASC",
        ]);
        $materialObj = new materialClass();
        //获取表头
        $checkAll = $this->checkAll("mxForm", "mxId");
        $tr[]     = [$checkAll, '分类', '产品编码', '产品名称', '规格型号'];
        //获取记录
        foreach ($page['array'] as $array) {
            $dominate = array(
                "box" => array("name" => "mxId", "value" => $array['id']),
            );
            $checkBox = $this->dominate($dominate);
            if (!empty($contractId) && $array['coid'] != self::$coid) {
                $checkBox = "<span class='no-choose'>--</span>";
            }
            $tr[] = array(
                $checkBox,
                $materialObj::getTypePath($array['typeId']),
                $array['code'],
                $array['name'],
                $array['model'],
            );
        }
        $json['html'] = $page['hint'] .
            "<form name='mxForm'>" .
            hidden(array("name" => "id", "value" => $this->post('id'))) .
            hidden(array("name" => "contractId", "value" => $this->post('contractId'))) .
            hidden(array('name' => 'khid', 'value' => $this->post('khid'))) .
            hidden(array('name' => 'isTax', 'value' => $this->post('isTax'))) .
            $this->tableMany(array("tr" => $tr, "showSort" => true, "page" => $page['para'])) .
            "</form>" .
            $this->js() .
            $page['button'];
        return json_encode($json, 320);
    }

    public function js()
    {
        return <<<efo
<script>
$(function (){
    $(".no-choose").parent().parent().css('color','#d7d7d7');
    $(".no-choose").parent().parent().attr('title','不是当前账套的产品，不可选择');
})
</script>
efo;
    }

    /**
     * 销售合同已进入发货单中的数量
     */
    private function contractInSendNum($sendOrderId, $contractMxId, $toUnit): float|int
    {
        $orderNum = 0;
        $all      = find(array(
            'table' => 'sale_send_order_detail',
            'where' => " orderId !='$sendOrderId' and saleOrderMxId = '$contractMxId' and coid = '" . self::$coid . "'"
        ));
        foreach ($all['array'] as $array) {
            $orderNum += machiningOrderClass::material_num_change($array['materialId'], $array['unit'], $toUnit, $array['num']);
        }
        return $orderNum;
    }

    /**
     * 编辑
     *
     * @return bool|string
     */
    public function addMxEdit()
    {
        $mxIdArr    = $this->post('mxId');
        $khid       = $this->post('khid');
        $isTax      = $this->post('isTax');
        $contractId = $this->post('contractId');//合同ID
        $id         = $this->post('id');
        if (!$this->power(self::page, 'edit')) {
            $json['warn'] = '你还没有编辑权限';
        } elseif (empty($mxIdArr) || !is_array($mxIdArr)) {
            $json['warn'] = '请选择产品';
        } else {
            $tr = [];
            foreach ($mxIdArr as $mxId) {
                $material   = query('material', "id = '$mxId'");
                $orderNum   = 0;
                $contractMx = [
                    'num'            => 0,
                    'price'          => 0,
                    'rate'           => 0,
                    'khMaterialCode' => '',
                    'khMaterialName' => '',
                ];
                if ($contractId) {
                    //如果是从合同明细中选择的，则带出合同明细的数据
                    $contractMx                   = query('sales_contract_detail', "materialId = '$mxId' and orderId = '$contractId'");
                    $contractMx['khMaterialName'] = $contractMx['aliasName'];
                    $contractMx['khMaterialCode'] = $contractMx['aliasNo'];
                    //已进入销售单的合同明细数量
                    $orderNum = $this->contractInSendNum($id, $contractMx['id'], $contractMx['unit']);
                    $unit     = $contractMx['unit'];
                } else {
                    $store_client_price           = query('store_client_price', "khid = '$khid' and materialId = '$material[id]' order by time desc");
                    $contractMx['khMaterialName'] = $store_client_price['materialAlias'];
                    $contractMx['khMaterialCode'] = $store_client_price['materialAliasNo'];
                    $contractMx['price']          = $store_client_price['dealPrice'];
                    $unit                         = $material['unit'];
                }

                $contractMx['num'] = $contractMx['num'] ?: 0;
                $price             = $contractMx['price'] ?
                    self::formattingFigure($contractMx['price'], 'pricePrecision') :
                    self::formattingFigure($material['salePrice'], 'pricePrecision');
                $num               = ($contractMx['num'] - $orderNum) > 0 ?
                    self::formattingFigure($contractMx['num'] - $orderNum, 'numPrecision') :
                    0;
                $rate              = $contractMx['rate'] ?: $material['saleRate'];
                $sourceId          = $contractMx['id'] ?? '';
                $result            = saleOrderClass::isTax($num, $price, $rate, $isTax);
                $money             = $result['money'];
                $taxMoney          = $result['taxMoney'];
                $moneyWithTax      = $result['moneyWithTax'];

                $select    = select(array('name' => 'rate[]', 'change' => 'countMoney', 'title' => '税率', 'option' => explode('，', para('taxRate'))));
                $suiji     = self::getId();
                $warehouse = select(array(
                    'class'   => 'width150',
                    'name'   => 'warehouseId[]',
                    'value'  => $material['warehouse'],
                    'change' => 'getInventory',
                    'option' => self::warehouseOption($material['id'], $unit),
                    'title'  => '仓库'
                ));
                if ($material['warehouse']) {
                    $positionId = inventoryBalanceClass::matchLocation($material['warehouse']);
                    $position   = componentClass::selectText([
                        'class'      => 'width150',
                        'name'      => 'positionId[]',
                        'value'     => $positionId,
                        'title' => warehousePositionClass::getName($positionId),
                        'cla'       => 'toolOverhaulOut',
                        'fun'       => 'getPositionByStock',
                        'change'       => 'getBatch',
                        'param'    => "{'materialId':'".$material['id']."','unit':'".$unit."','warehouseId':'".$material['warehouse']."','zero':'0'}",
                        'attach'    => "<label class='checkbox'><input name='zero' type='checkbox' value='1'><span></span>&nbsp;显示零/负库存</label>",
                    ]);
                    $result        = $this->getInventory($material['warehouse'], $material['id'], $material['unit']);
                    $result        = json_decode($result, true);
                    $result['num'] = $result['num'] ?? 0;
                    $batch                       =
                        componentClass::selectText([
                            'class'      => 'width150',
                            'name'      => 'batch[]',
                            'value'     => '',
                            'title' => '',
                            'cla'       => 'toolOverhaulOut',
                            'fun'       => 'getBatchByStock',
                            'change'       => 'getBatchInventory',
                            'param'    => "{'materialId':'".$material['id']."','unit':'".$unit."','warehouseId':'".$material['warehouse']."','positionId':'".$positionId."'}",
                        ]);
                } else {
                    $position   = componentClass::selectText([
                        'class'      => 'width150',
                        'name'      => 'positionId[]',
                        'value'     => '',
                        'title' => '',
                        'cla'       => 'toolOverhaulOut',
                        'fun'       => 'getPositionByStock',
                        'change'       => 'getBatch',
                        'param'    => "{'materialId':'".$material['id']."','unit':'".$unit."','warehouseId':'##','zero':'0'}",
                        'attach'    => "<label class='checkbox'><input name='zero' type='checkbox' value='1'><span></span>&nbsp;显示零/负库存</label>",
                    ]);
                    $result['num'] = 0;
                    $batch                       =
                        componentClass::selectText([
                            'class'      => 'width150',
                            'name'      => 'batch[]',
                            'value'     => '',
                            'title' => '',
                            'cla'       => 'toolOverhaulOut',
                            'fun'       => 'getBatchByStock',
                            'change'       => 'getBatchInventory',
                            'param'    => "{'materialId':'".$material['id']."','unit':'".$unit."','warehouseId':'##','positionId':'##'}",
                        ]);
                }
                $tr[] = [
                    componentClass::tablePperation('', "delTableRow(this);calculateTotal()") .
                    hidden(array('name' => 'mxId[]', 'value' => $suiji)) .
                    hidden(array('name' => 'materialId[]', 'value' => $mxId)) .
                    hidden(array('name' => 'sale_detail_id[]', 'value' => $sourceId)).
                    hidden(array('name' => 'saleOrderId[]', 'value' => '')),
                    $material['code'],
                    $material['name'],
                    $material['model'],
                    select(array(
                        'name'   => 'unitDetail[]',
                        'data'   => ['unit' => $unit],
                        'change' => 'unitChange',
                        'option' => machiningOrderClass::unitOption($material['id']),
                        'title'  => '单位',
                        'value'  => $unit
                    )),
                    $warehouse,
                    "<span class='inventory'>" . self::lostZero($result['num'], 'numPrecision') . "</span>",
                    "<span class='position'>{$position}</span>",
                    "<span class='batch'>{$batch}</span>",
                    "<span class='batchInventory'></span>",
                    text(array('name' => 'num[]', 'type' => 'text', 'blur' => 'inventoryWarning();countMoney', 'other' => self::digitalFiltering('numPrecision'), 'value' => $num)),
                    text(array('name' => 'packing_quantity[]', 'type' => 'text', 'other' => self::digitalFiltering('numPrecision'), 'value' => '')),
                    text(array('name' => 'box_capacity[]', 'type' => 'text', 'other' => self::digitalFiltering('numPrecision'), 'value' => '')),
                    text(array('name' => 'remainder[]', 'type' => 'text', 'other' => self::digitalFiltering('numPrecision'), 'value' => '')),
                    text(array('name' => 'price[]', 'type' => 'text', 'blur' => 'countMoney', 'other' => self::digitalFiltering('pricePrecision'), 'value' => $price)),
                    "<span class='rowMoney'>{$money}</span>",
                    $select,
                    "<span class='taxMoney'>{$taxMoney}</span>",
                    text(array('name' => 'moneyWithTax[]', 'type' => 'text', 'blur' => 'calculatePrice', 'other' => self::digitalFiltering('amount'), 'value' => $moneyWithTax)),
                    '',
                    text(array('name' => 'khMaterialCode[]', 'value' => $contractMx['khMaterialCode'])),
                    text(array('name' => 'khMaterialName[]', 'value' => $contractMx['khMaterialName'])),
                    text(array('name' => 'mxText[]', 'value' => '')),
                ];
            }
            $json['warn'] = 2;
            $json['html'] = $this->tableMany(['tr' => $tr, 'thead' => false]);
        }
        return json_encode($json, 320);
    }


    /*详情页*/
    public function detail(): string
    {
        $info = query(self::table, " id = '{$this->get('id')}'");
        if (empty($info['id'])) {
            $html = '未找到此' . self::title;
        } else {
            //菜单
            $data = array(
                "id" => $info['id'],
            );

            $button = [];

            if ($this->power(self::page, "confirm") && $info['state'] == '待确认') {
                $button[] = [
                    "name"  => "复核",
                    "type"  => "弹出层",
                    "cla"   => $this->get('cla'),
                    "fun"   => "operate",
                    "post"  => ["act" => 'confirm', "id" => $info['id']],
                    'style' => 'li-btn-blue-bg',
                ];
            }

            if ($info['state'] == '草稿') {
                if ($this->power(self::page, "edit")) {
                    //按钮
                    $button[] = [
                        "name"  => "提交",
                        "type"  => "弹出层",
                        "cla"   => $this->get('cla'),
                        "fun"   => "operate",
                        "post"  => ["act" => 'commit', "id" => $info['id']],
                        'style' => 'li-btn-blue-bg',
                    ];
                    //按钮
                    $button[] = [
                        "name"  => "编辑",
                        "type"  => "顶部页卡",
                        "title" => "编辑用户",
                        "url"   => $this->root . "control/adIndex.html?cla={$this->get('cla')}&fun=edit&id=" . $info['id'],

                    ];
                }
                if ($this->power(self::page, "del")) {
                    //按钮
                    $button[] = [
                        "name" => "删除",
                        "type" => "弹出层",
                        "cla"  => $this->get('cla'),
                        "fun"  => "operate",
                        "post" => ["act" => 'del', "id" => $info['id']],
                    ];
                }
            }
            //按钮
            $button[] = [
                "name" => "打印",
                "type" => "点击事件",
                "fun"  => "window.open('" . root . "control/adIndex.html?cla=" . self::page . "&fun=printPage&id={$info['id']}')",
            ];
            $button[] = [
                "name" => "打印产品标识卡",
                "type" => "外部链接",
                "url"  => root . 'control/adIndex.html?cla=' . $this->get('cla') . '&fun=printCard&body=' . $info['id'],
            ];
            if ($this->power(self::page, "cancel") && $info['state'] != '草稿') {
                $button[] = [
                    "name" => "撤回",
                    "type" => "弹出层",
                    "cla"  => $this->get('cla'),
                    "fun"  => "operate",
                    "post" => ["act" => 'cancel', "id" => $info['id']],
                ];
            }
            $button[] = [
                "name"  => "单据追踪",
                "type"  => "顶部页卡",
                "title" => "单据追踪",
                "url"   => $this->root . "control/adIndex.html?cla=orderTrack&fun=home&orderType=销售发货单&id=" . $info['id'],
            ];
            //操作记录
            $button[] = self::operateLogBut($info['id'], 1);
            $sales_contract = query('sales_contract', "id = '$info[contractId]'");
            $tr[]           = ['产品编码', '产品名称', '规格型号', '单位', '发货数量', '变更数量','重量（吨）', '100L钢瓶', '400L钢瓶', '800L钢瓶', '关联销售订单'];
            $result         = find(array('table' => self::tableMx, 'where' => "orderId = '$info[id]' order by list"));
            foreach ($result['array'] as $array) {
                $material    = query('material', " id = '$array[materialId]'");
                $saleOrderMx = query("sale_order_detail", "id = '$array[saleOrderMxId]'");
                $saleOrder   = query('sale_order', "id = '$saleOrderMx[orderId]'");
                //当前可用库存
                $tr[] = [
                    $material['code'],
                    $material['name'],
                    $material['model'],
                    $array['unit'],
                    self::lostZero($array['num'], 'numPrecision'),
                    $saleOrderMx['change_num'],
                    $array['weight'],
                    $array['one_hundred_l'],
                    $array['four_hundred_l'],
                    $array['eight_hundred_l'],
                    $saleOrder['orderNo'],
                ];
            }
            $total               = find(array(
                'table'  => self::tableMx,
                'field'  => "ifnull(sum(num),0) as num,ifnull(sum(money),0) as totalMoney,ifnull(sum(taxMoney),0) as taxMoney,ifnull(sum(money-taxMoney),0) as money",
                'single' => "orderId = '$info[id]'"
            ));
            $total['num']        = $total['num'] ?? 0;
            $total_tr            = [
                4  => self::lostZero($total['num'], 'numPrecision'),
            ];
            $kehu                = query('kehu', "khid = '$info[khid]'");
            $kehu['credits']     = $kehu['credits'] ?: 0;

            $contractDetail = $sales_contract['id'] ? $this->dominate(["detail" => ["title" => '合同详情', "click" => "newIframe('合同详情','" . root . 'control/adIndex.html?cla=salesContract&fun=detail&id=' . $sales_contract['id'] . "')"]]) : '';
            //生成表单
            $data['button'] = $this->tableShowButton($button);
            $logistics_car = query('logistics_car', " id = '$info[car_id]'");
            $departmentId = query('department', "id = '$info[departmentId]'");
            $data['html']   = $this->tableShow(array(
                '详细信息' => array(
                    array(
                        "客户"   => $kehu['companyName'],
                        "信用额度" => $kehu['credits'] . '/0',
                        "合同名称" => $sales_contract['name'].$contractDetail,
                        "单据日期" => self::null($info['recordDate']),
                    ),
                    array(
                        "送货时限"   => self::null($info['limitTime']),
                        "交货方式"   => saleOrderClass::deliveryMethod[$info['delivery_method']],
                        "结算方式"   => $info['settlementType'] . ($info['settlementType'] == '月结' ? '-' . $info['settlementDay'] . '天' : ''),
                        "实际交货日期" => self::null($info['actual_delivery_date']),
                    ),
                    array(
                        "收货人"  => $info['contactName'],
                        "收货电话" => $info['contactTel'],
                        "收货地址" => $info['address'],
                        "单据编号" => $info['orderNo'],
                    ),
                    array(
                        "制单人" => self::findStaff($info['stid']),
                        "状态"  => $info['state'],
                        "销售类型"  => $info['orderType'],
                        "销售部门"  => $departmentId['name'],
                    ),
                    array(
                        "驾驶员" => self::findStaff($info['delivery_name']),
                        "押运员"  => self::findStaff($info['delivery_manage']),
                        "车辆"  => $logistics_car['shortName'].$logistics_car['carNo'],
                    ),
                    array(
                        "备注" => $info['text'],
                    ),
                    array(
                        '单据标签' => orderTagClass::getTagName($info['id']),
                    ),
                ),
            ));
            $data['html']   .= $this->detailList([
                'title' => '发货明细',
                'html'  => $this->listTable(array('tr' => $tr, 'showEmpty' => true, 'total' => $total_tr, 'showSort' => true)),
            ]);
            $data['html']   .= $this->detailBlock([
                '附件' => fileUploadClass::showFile($info['id'], '销售发货单')
            ]);
            //生成页面
            $html = $this->tab($data);
        }
        //返回
        return $html;
    }

    /**
     * 确认
     */
    public function confirmEdit()
    {
        $id    = $this->post('id');
        $info  = query(self::table, " id = '$id'");
        $field = [
            'state'       => '已确认',
            'checkId'     => self::$stid,
            'confirmTime' => $this->time,
        ];
        //判断库存是否可发货
        $warn         = '';
        $outAllowZero = optionSettingClass::getSettingItem('库存', 'outAllowZero');//允许零库存出库
        //判断对应批次的数量是否超出库存
        if (!$outAllowZero) {
            $result          = find(array('table' => self::tableMx, "where" => "orderId = '$id' order by list asc"));
            $tempResult      = [];
            $tempBatchResult = [];
            //判断库存是否可发货,首先需要相同物料分组，避免只判断一个物料够不够
            foreach ($result['array'] as $array) {
                $material = query('material', "id = '$array[materialId]'", "unit");
                $num      = machiningOrderClass::material_num_change($array['materialId'], $array['unit'], $material['unit'], $array['num']);
                // 仓库数量汇总
                $tempResult[$array['materialId']][$array['warehouseId']][] = [
                    'id'  => $array['id'],
                    'num' => $num,
                ];
                // 批次数量汇总
                $tempBatchResult[$array['materialId']][$array['warehouseId']][$array['batch']][] = [
                    'id'  => $array['id'],
                    'num' => $num,
                ];
            }
            //判断批次库存
            foreach ($tempBatchResult as $materialId => $array) {
                foreach ($array as $warehouseId => $childRes) {
                    foreach ($childRes as $batch => $val) {
                        $material       = query('material', "id = '$materialId'", 'name,unit');
                        $batchInventory = inventoryBalanceClass::inventoryNum($materialId, $warehouseId, batch: $batch);
                        $mainUnit       = array_sum(array_column($val, 'num'));
                        if ($mainUnit > $batchInventory) {
                            $warn = "物料【{$material['name']}】发货数量已超出库存数量";
                            break 3;
                        }
                    }
                }
            }

            //库存可用量控制
            $inventorySetting = optionSettingClass::getSetting('库存');
            if (empty($warn) && isset($inventorySetting['inventoryCtrl']['option']) && $inventorySetting['inventoryCtrl']['option'] && in_array('saleSendOrder', (array)$inventorySetting['inventoryCtrl']['val'])) {
                foreach ($tempResult as $materialId => $array) {
                    foreach ($array as $warehouseId => $childRes) {
                        $material = query('material', "id = '$materialId'", "name,unit");
                        //当前可用库存
                        $inventoryNum = machiningOrderClass::inventory($materialId, $material['unit'], $warehouseId);
                        $num          = array_sum(array_column($childRes, 'num'));
                        if (self::formattingFigure($num, 'numPrecision') > self::formattingFigure($inventoryNum, 'numPrecision')) {
                            $warn = "物料【{$material['name']}】发货数量已超出可用库存数量";
                            break 2;
                        }
                    }
                }
            }
        }
        //判断客户信用额度
        $setting = optionSettingClass::getSetting('销售');
        if (empty($warn) && isset($setting['creditCtrl']['option']) && $setting['creditCtrl']['option'] && $setting['creditCtrl']['way'] == '复核') {
            $kehu    = query('kehu', "khid = '$info[khid]'");
            $balance = saleOrderClass::clientBalance($info['khid']);
            if ($kehu['credits'] < round($balance, 2)) {
                $warn = '客户信用额度不足';
            }
        }
        if (!$this->power(self::page, 'confirm')) {
            $json['warn'] = '你还没有确认权限';
        } elseif (empty($info['id'])) {
            $json['warn'] = '未找到此销售发货单';
        } elseif ($info['state'] != '待确认') {
            $json['warn'] = '此发货单不是待确认状态，不能确认';
        } elseif ($warn) {
            $json['warn'] = $warn;
        } else {
            $this->startTrans();
            $json['warn'] = update(array(
                "table" => self::table,
                "field" => $field,
                "where" => "id = '$info[id]'",
            ));
            //生成出库单
            $bool = $this->insertInventory($info);
            if ($bool !== true) {
                $json['warn'] = $bool;
            }
            //更新订单实际交货情况
            $this->updateDeliveryDate($info);
            //生成物流发货单
            $optionSetting = optionSettingClass::getSetting('物流');  //选项设置
            $sourceOrder   = $optionSetting['sourceOrder'] ?: [];     //物流发货依据
            if ($info['delivery_method'] == 2 && in_array('saleSendOrder', $sourceOrder)) {
                $bool = self::delivery_order($info);
                if ($bool !== true) {
                    $json['warn'] = $bool;
                }
            }
            if (in_array($json['warn'], ["新增成功", "更新成功"])) {
                $this->commitTrans();
                // 单据是否需要复核
                if (!in_array(self::page, self::$publicSettings['documentReview'])) {
                    self::insertOperateLog($info['id'], self::title, "提交、复核");
                } else {
                    self::insertOperateLog($info['id'], self::title, "复核");
                }
                //添加日志
                $type = mb_substr($json['warn'], 0, 2, 'utf-8');
                $text = $this->control['name'] . $type . "了" . self::title . "id：【" . $id . '】';
                $this->logText(["type" => self::page, "text" => $text]);
                //返回
                $_SESSION['warn'] = $json['warn'];
                $json['warn']     = 2;
                $json['href']     = root . "control/adIndex.html?cla=" . $this->get('cla') . "&fun=detail&id=" . $info['id'];
            } else {
                $this->rollback();
            }
        }
        resp:
        return json_encode($json, 320);
    }

    /**
     * 更新订单实际交货日期
     */
    private function updateDeliveryDate($info): bool|string
    {
        $result        = find(array('table' => self::tableMx, 'where' => "orderId = '$info[id]' order by list asc"));
        $sale_order_id = [];
        foreach ($result['array'] as $array) {
            if ($array['sourceTable'] == 'sale_order_detail') {
                $sale_order_detail = query('sale_order_detail', "id = '$array[saleOrderMxId]'", "id,orderId,num,send_num");
                $sendTotal         = find(array(
                    'table'  => self::tableMx,
                    'single' => " saleOrderMxId = '$sale_order_detail[id]' and orderId in (select id from sale_send_order where coid = '" . self::$coid . "' and state = '已确认') ",
                    'field'  => "ifnull(sum(num),0) as num"
                ));
                // 增加销售订单的已发货数量
                update(array(
                    "table" => 'sale_order_detail',
                    "field" => ['send_num' => $sendTotal['num']],
                    "where" => "id = '$sale_order_detail[id]'",
                ));
                if (!in_array($sale_order_detail['orderId'], $sale_order_id)) $sale_order_id[] = $sale_order_detail['orderId'];
            }
        }
        //修改主订单
        foreach ((array)$sale_order_id as $orderId) {
            $sale_order               = query('sale_order', "id = '$orderId'", "id,deliveryDate,actual_delivery_date");
            $total                    = find(array(
                'table'  => 'sale_order_detail',
                'single' => "orderId = '$orderId'",
                'field'  => "ifnull(sum(num),0) as num,ifnull(sum(send_num),0) as send_num,ifnull(sum(refund_num),0) as refund_num"
            ));
            $field                    = [];
            $field['send_num']        = $total['send_num'];
            $field['refund_num']      = $total['refund_num'];
            $residue                  = $total['num'] - $total['send_num'] + $total['refund_num'];
            $field['completion_rate'] = round(($total['send_num'] - $total['refund_num']) / $total['num'], 4);
            // 是否按期交货
            $field['actual_delivery_date'] = $info['actual_delivery_date'];
            if ($residue <= 0 && strtotime($this->date) <= strtotime($sale_order['deliveryDate'])) {
                $field['deliver_on_schedule'] = 1;
            } else {
                $field['deliver_on_schedule'] = 0;
            }
            // 自动关闭销售订单
            $saleClose = optionSettingClass::getSettingItem('销售', 'saleClose');
            if ($residue <= 0 && $saleClose) {
                $field['isClose'] = 1;
            }
            if (!empty($field)) {
                update(array(
                    "table" => 'sale_order',
                    "field" => $field,
                    "where" => "id = '$sale_order[id]'",
                ));
            }

        }
        return true;
    }

    /**
     * 生成物流发货单
     */
    public static function delivery_order($info): bool|string
    {
        $field            = [
            'id'          => suiji(),
            'coid'        => self::$coid,
            'sourceTable' => 'sale_send_order',
            'sourceId'    => $info['id'],
            'khid'        => $info['khid'],
            'manager'     => $info['manager'],
            'contractId'  => $info['contractId'],
            'recordDate'  => $info['recordDate'],
            'limitTime'   => $info['limitTime'],
            'text'        => $info['text'],
            'contactName' => $info['contactName'],
            'contactTel'  => $info['contactTel'],
            'address'     => $info['address'],
            'money'       => $info['money'],
            'isTax'       => $info['isTax'],
            'stid'        => $info['stid'],
            'updateTime'  => date("Y-m-d H:i:s"),
            'time'        => date("Y-m-d H:i:s"),
        ];
        $field['orderNo'] = codeTemplateClass::generateCode('deliveryManage', [
            'prefix' => 'XSFH' . date('Ymd', strtotime($field['recordDate'])),
            'table'  => 'delivery_order',
            'field'  => 'orderNo',
            'where'  => " and coid = '" . self::$coid . "'",
        ], $field);
        $warn             = static::insert(array(
            'table' => 'delivery_order',
            'field' => $field,
        ));
        if ($warn != '新增成功') {
            return '生成物流发货单失败';
        }

        $all = find(array(
            'table' => "sale_send_order_detail as a",
            'field' => "a.*",
            'where' => "a.orderId = '$info[id]' and a.coid = '" . self::$coid . "' order by a.list asc",
        ));
        foreach ($all['array'] as $array) {
            $warn = static::insert(array(
                'table' => 'delivery_order_detail',
                'field' => [
                    'id'              => suiji(),
                    'coid'            => self::$coid,
                    'orderId'         => $field['id'],
                    'sourceTable'     => 'sale_send_order_detail',
                    'sourceId'        => $array['id'],
                    'saleOrderMxId'   => $array['saleOrderMxId'],
                    'khMaterialCode'  => $array['khMaterialCode'],
                    'khMaterialName'  => $array['khMaterialName'],
                    'materialId'      => $array['materialId'],
                    'warehouseId'     => $array['warehouseId'],
                    'num'             => $array['num'],
                    'unit'            => $array['unit'],
                    'price'           => $array['price'],
                    'taxMoney'        => $array['taxMoney'],
                    'moneyWithOutTax' => $array['moneyWithOutTax'],
                    'rate'            => $array['rate'],
                    'money'           => $array['money'],
                ],
            ));
            if ($warn != '新增成功') {
                return '生成物流发货明细失败';
            }
        }
        return true;
    }

    /**
     * 生成出库单
     *
     */
    private function insertInventory($info): bool|string
    {
        $quality  = [];//质检分组
        $re_array = [];//根据仓库重新分组
        $result   = find(array('table' => self::tableMx, 'where' => "orderId = '$info[id]' order by list asc"));
        foreach ($result['array'] as $array) {
            $material = query('material', "id = '$array[materialId]'", "OQC");
            if ($material['OQC']) {
                //质检
                $quality[$array['materialId']][$array['id']] = ['num' => $array['num']];
            }
            $re_array[$array['warehouseId']][] = $array['id'];
        }
        $inventory_to_quality = [];//把库存ID插入质检单中
        $kehu                 = query('kehu', "khid = '$info[khid]'");
        $state                = '待出库';
        $confirmTime          = null;
        // 确认发货时自动销售出库
        $autoOutStock = optionSettingClass::getSettingItem('物流', 'autoOutStock');
        // 有发货质检，并且出库前需要先完成质检，则不会自动出库
        if(!empty($quality) && optionSettingClass::getSettingItem('库存', 'outStockQuality')){
            $autoOutStock = false;
        }
        if ($autoOutStock) {
            $state       = '已出库';
            $confirmTime = $info['actual_delivery_date'] ? $info['actual_delivery_date'] . " " . date("H:i:s") : $this->time;
        }
        $index = 1;
        foreach ($re_array as $warehouseId => $array) {
            $id               = self::getId();
            $field            = [
                'id'               => $id,
                'type'             => '销售发货',
                'coid'             => self::$coid,
                'sourceTable'      => self::table,
                'sourceId'         => $info['id'],
                'warehouseId'      => $warehouseId,
                'targetId'         => $info['khid'],
                'classify'         => 0,
                'confirmTime'      => $confirmTime,
                'state'            => $state,
                'workFlow'         => !empty($quality) ? '待质检' : '已质检',
                'stid'             => self::$stid,
                'orderDate'        => $this->date,
                'sourceDate'       => $info['recordDate'],
                'sourceNo'         => $info['orderNo'],
                'sourceTargetName' => $kehu['companyName'],
            ];
            $userNo           = codeTemplateClass::generateCode('outStock', [
                'prefix' => 'KCCK' . date('Ymd'),
                'table'  => 'inventory',
                'field'  => 'orderNo',
                'where'  => " and coid = '" . self::$coid . "'",
                'index'  => $index,
            ], $field);
            $field['orderNo'] = $userNo;
            $warn             = static::insert(array(
                'table' => 'inventory',
                'field' => $field,
            ));
            if ($warn != '新增成功') {
                return "新增出库单失败";
            }
            //自动出库则生成出库变动记录
            if ($state == '已出库') {
                $changeId               = self::getId();
                $fieldChange            = [
                    'id'               => $changeId,
                    'coid'             => self::$coid,
                    'type'             => '销售发货',
                    'classify'         => 0,
                    'targetId'         => $info['khid'],
                    'sourceTargetName' => $kehu['companyName'],
                    'confirmTime'      => $confirmTime,
                    'reviewTime'       => $confirmTime,
                    'state'            => '已复核',
                    'stid'             => self::$stid,
                    'time'             => $this->date,
                ];
                $userNo                 = codeTemplateClass::generateCode('outStock', [
                    'prefix' => 'CKD' . date('Ymd'),
                    'table'  => 'inventory_changes',
                    'field'  => 'orderNo',
                    'where'  => " and coid = '" . self::$coid . "'",
                    'index'  => $index,
                ], $fieldChange);
                $fieldChange['orderNo'] = $userNo;
                $warn                   = static::insert(array(
                    'table' => 'inventory_changes',
                    'field' => $fieldChange,
                ));
                if ($warn != '新增成功') {
                    return "新增出库变动失败";
                }
            }
            $index++;
            foreach ($array as $mxId) {
                $mx = query(self::tableMx, "id = '$mxId'");
                //$mx['price'] = inventoryDefaultClass::pricingRules('outStockPrice',$mx['materialId'],$mx['unit']);
                $inventory_to_quality[$mxId] = $id;
                $num                         = 0;
                $money                       = 0;
                if ($autoOutStock) {
                    $num   = $mx['num'];
                    $money = self::formattingFigure($num * $mx['price'] * (1 + $mx['rate']));
                }
                $warn = static::insert(array(
                    'table' => 'inventory_detail',
                    'field' => $fieldMx = [
                        'id'          => self::getId(),
                        'coid'        => self::$coid,
                        'sourceTable' => self::tableMx,
                        'sourceId'    => $mx['id'],
                        'materialId'  => $mx['materialId'],
                        'unit'        => $mx['unit'],
                        'warehouseId' => $warehouseId,
                        'positionId'  => $mx['positionId'],
                        'orderId'     => $id,
                        'orderNum'    => $mx['num'],
                        'batch'       => $mx['batch'],
                        'num'         => $num,
                        'rate'        => $info['isTax'] ? 0 : $mx['rate'],
                        'price'       => $mx['price'],
                        'money'       => $money,
                        'quality'       => isset($quality[$mx['materialId']][$mxId]) ? 1 : 0,
                        'pass_num'      => isset($quality[$mx['materialId']][$mxId]) ? 0 : $mx['num'],
                    ],
                ));
                if ($warn != '新增成功') {
                    return "新增出库单明细失败";
                }
                if ($autoOutStock && $num > 0) {
                    //生成出库变动记录
                    $avgPrice = inventoryBalanceClass::unitPrice($mx['materialId']);
                    $warn     = static::insert(array(
                        'table' => 'inventory_changes_detail',
                        'field' => $fieldChangeMx = [
                            'id'          => self::getId(),
                            'coid'        => self::$coid,
                            'orderId'     => $changeId,
                            'materialId'  => $mx['materialId'],
                            'orderNum'    => $mx['num'],
                            'batch'       => $mx['batch'],
                            'num'         => $num,
                            'unit'        => $mx['unit'],
                            'price'       => $avgPrice,
                            'money'       => self::formattingFigure($avgPrice * $num),
                            'sourceTable' => 'inventory_detail',
                            'sourceId'    => $fieldMx['id'],
                            'sourceNo'    => $field['orderNo'],
                            'warehouseId' => $warehouseId,
                            'positionId'  => $mx['positionId'],
                            'time'        => $this->date,
                        ],
                    ));
                    if ($warn != '新增成功') {
                        return "新增出库变动明细失败";
                    }
                    // 关联表
                    $targetDate = $targetNo = $targetText = $hiddenText = ' ';
                    if ($field['sourceTable']) {
                        $obj = query($field['sourceTable'], " id = '{$field['sourceId']}' ");
                        if (!empty($obj['id'])) {
                            $targetDate = $obj['recordDate'];
                            $targetNo   = $obj['orderNo'];
                            $targetText = $field['type'] . "单【{$obj['orderNo']}】";
                            $hiddenText = $field['type'] . '单ID：' . $obj['id'] . '，出库单ID：' . $fieldMx['orderId'] . '出库单子ID：' . $fieldMx['id'];
                        }
                    }
                    $bool = supplierClass::insertRecord(
                        $field['targetId'],
                        $field['type'],
                        self::formattingFigure($num * $fieldMx['price'] * (1 + $fieldMx['rate'])),
                        'inventory_detail',
                        $fieldMx['id'],
                        $targetDate,
                        $targetNo,
                        $targetText,
                        $hiddenText,
                        $fieldChangeMx['id']
                    );
                    if ($bool !== true) {
                        return $bool;
                    }
                    $outAllowZero = optionSettingClass::getSettingItem('库存', 'outAllowZero'); // 仓库允许零库存出库
                    $mainUnitNum  = inventoryBalanceClass::toMainUnit($fieldMx['materialId'], $num, $fieldMx['unit']);
                    if (!$outAllowZero && inventoryBalanceClass::inventoryNum($fieldMx['materialId'], $fieldMx['warehouseId'], batch: $mx['batch']) - $mainUnitNum < 0) {
                        return '出库数量不能大于库存数量';
                    }
                    $bool = inventoryBalanceClass::inventoryControl($fieldMx['materialId'], $mainUnitNum, "down");  //最低库存控制
                    if ($bool !== true) {
                        return $bool;
                    }
                    if ($mainUnitNum > 0) {
                        $positionId = $fieldMx['positionId'];
                        insert([
                            'table' => 'inventory_record',
                            'field' => [
                                'id'            => static::getNo(),
                                'coid'          => static::$coid,
                                'warehouseId'   => $fieldMx['warehouseId'],
                                'positionId'    => $positionId,
                                'materialId'    => $fieldMx['materialId'],
                                'batch'         => $fieldMx['batch'],
                                'num'           => -1 * $mainUnitNum,
                                'inventoryId'   => $fieldMx['orderId'],
                                'inventoryMxId' => $fieldMx['id'],
                                'changesId'     => $fieldChangeMx['id'],
                                'defaultPrice'  => $avgPrice,
                                'time'          => $confirmTime,
                            ],
                        ]);
                        if (!empty($positionId)) {
                            inventoryBalanceClass::recordStockToLocation(
                                '',
                                [
                                    'materialId'  => $fieldMx['materialId'],
                                    'warehouseId' => $fieldMx['warehouseId'],
                                    'positionId'  => $positionId,
                                    'batch'       => $fieldMx['batch']
                                ]);
                        }
                    }
                }
            }
        }
        //质检
        $index = 1;
        foreach ($quality as $materialId => $array) {
            foreach ($array as $mxId => $item) {
                $mx       = query(self::tableMx, "id = '$mxId'");
                $material = query('material', "id = '$materialId'");
                //$allTypeId   = machiningInOrderClass::getAllParentTypeId($material['typeId']);
                $allTypeId = explode('/', $material['typePath']);
                //查找质检方案
                $quarantine = query(
                    'quarantine',
                    "application like '%发货检验%' and coid = '" . self::$coid . "' and state = '启用' and startDate<= date_format(now(),'%Y-%m-%d') and endDate>date_format(now(),'%Y-%m-%d') and if(materialId!='',materialId = '$material[id]',materialTypeId in ('" . implode("','", $allTypeId) . "')) order by materialId desc,list desc,time desc"
                );
                if (empty($quarantine['id'])) {
                    return "未找到物料【{$material['name']}】的质检方案";
                }
                //根据质检批量分成几组
                $num    = $quarantine['qualityBatch'] > 0 ? ceil($item['num'] / $quarantine['qualityBatch']) : 1;
                $userNo = codeTemplateClass::generateCode('shippingInspection', [
                    'prefix' => 'OU' . date('Ymd'),
                    'table'  => 'quality_order',
                    'field'  => 'orderNo',
                    'where'  => " and coid = '" . self::$coid . "'",
                    'index'  => $index,
                ], []);
                $index++;
                if ($num > 100) {
                    return "质检批量过小，新增质检单数量超过100条";
                }
                for ($i = 1; $i <= $num; $i++) {
                    $id        = self::getId();
                    $remindNum = ($item['num'] - ($i - 1) * $quarantine['qualityBatch']);
                    $warn      = static::insert(array(
                        'table' => 'quality_order',
                        'field' => [
                            'id'          => $id,
                            'type'        => 3,
                            'coid'        => self::$coid,
                            'warehouseId' => $mx['warehouseId'],
                            'inventoryId' => $inventory_to_quality[$mx['id']] ?? '',
                            'sourceTable' => self::table,
                            'sourceId'    => $info['id'],
                            'sourceMxId'  => $mx['id'],
                            'targetId'    => $info['khid'],
                            'num'         => $remindNum > $quarantine['qualityBatch'] && $quarantine['qualityBatch'] > 0 ? $quarantine['qualityBatch'] : $remindNum,
                            'materialId'  => $material['id'],
                            'unit'        => $mx['unit'],
                            'orderNo'     => $userNo,
                            'batch'       => $userNo . '-' . $i,
                            'qid'         => $quarantine['id'],
                            'sourceNo'    => $info['orderNo'],
                            'state'       => '待质检',
                        ],
                    ));
                    if ($warn != '新增成功') {
                        return "新增质检单失败";
                    }
                    $all = find(array('table' => 'quarantine_item', 'where' => "orderId = '$quarantine[id]' order by list"));
                    foreach ($all['array'] as $val) {
                        $warn = static::insert(array(
                            'table' => 'quality_order_item',
                            'field' => [
                                'id'      => self::getId(),
                                'coid'    => self::$coid,
                                'orderId' => $id,
                                'itemId'  => $val['id'],
                            ],
                        ));
                        if ($warn != '新增成功') {
                            return "新增质检单明细质检项目失败";
                        }
                    }
                }
            }
        }
        return true;
    }


    /**
     * 撤回
     */
    public function cancelEdit()
    {
        $id    = $this->post('id');
        $info  = query(self::table, " id = '$id'");
        $field = [
            'state'       => '草稿',
            'checkId'     => '',
            'confirmTime' => '',
        ];
        if (!$this->power(self::page, 'cancel')) {
            $json['warn'] = '你还没有撤回权限';
        } elseif (empty($info['id'])) {
            $json['warn'] = '未找到此销售发货单';
        } elseif (static::total(array('table' => 'inventory', "where" => "sourceId = '$info[id]' and coid = '" . self::$coid . "' and state in ('部分出库','已出库')")) > 0) {
            $json['warn'] = '此发货单已存在有出库的单据，不能撤回';
        } elseif (static::total(array('table' => 'quality_order', "where" => "sourceId = '$info[id]' and coid = '" . self::$coid . "' and state ='已质检'")) > 0) {
            $json['warn'] = '此发货单已存在有已质检的单据，不能撤回';
        } else {
            $json['warn'] = update(array(
                "table" => self::table,
                "field" => $field,
                "where" => "id = '$info[id]'",
            ));
            if (in_array($json['warn'], ["新增成功", "更新成功"])) {
                //更新订单实际交货日期
                $this->updateDeliveryDate($info);
                //删除出库单
                delete(array('table' => 'inventory_detail', 'where' => "orderId in (select id from inventory where sourceId = '$info[id]' and coid = '" . self::$coid . "')"));
                delete(array('table' => 'inventory', 'where' => "sourceId = '$info[id]'"));
                //删除质检单
                delete(array('table' => 'quality_order_item', 'where' => "orderId in (select id from quality_order where sourceId = '$info[id]' and coid = '" . self::$coid . "')"));
                delete(array('table' => 'quality_order_reason', 'where' => "orderId in (select id from quality_order where sourceId = '$info[id]' and coid = '" . self::$coid . "')"));
                delete(array('table' => 'quality_order', 'where' => "sourceId = '$info[id]'"));
                self::insertOperateLog($info['id'], self::title, "撤回");
                //添加日志
                $type = mb_substr($json['warn'], 0, 2, 'utf-8');


                $text = $this->control['name'] . $type . "了" . self::title . "id：【" . $id . '】';
                $this->logText(["type" => self::page, "text" => $text]);
                //返回
                $_SESSION['warn'] = $json['warn'];
                $json['warn']     = 2;
                $json['href']     = root . "control/adIndex.html?cla=" . $this->get('cla') . "&fun=detail&id=" . $info['id'];
            }
        }
        resp:
        return json_encode($json, 320);
    }

    /**
     * Desc : 提交前数据验证
     * User : you name
     * Date : 2023-11-17 16:04
     * @param $info
     * @return string|bool
     */
    public function dataValidation($info): string|bool
    {
        $annotation = [
            'khid'                 => '客户',
            'recordDate'           => '单据日期',
            'delivery_method'      => '交货方式',
            'settlementType'       => '结算方式',
            'actual_delivery_date' => '实际交货日期',
        ];
        $rules      = [
            'khid'                 => 'required',
            'recordDate'           => 'required|date',
            'delivery_method'      => 'required',
            'settlementType'       => 'required',
            'actual_delivery_date' => 'required|date',
        ];
        // 校验是否符合规则
        $vt     = new \verify();
        $result = $vt->setAliases($annotation)->validate($info, $rules)->isOk();
        if ($result === false) {
            return $vt->getErrs()[0];  // 多个规则提示最前面一条
        }
        $result = find(array('table' => self::tableMx, 'field' => 'id,materialId,num,unit,price,warehouseId', 'where' => "orderId = '$info[id]' order by list"));
        if (count($result['array']) == 0) {
            return "请完善发货明细";
        }
        if (static::is_null_array(array_column($result['array'], 'materialId'))) {
            return "发货明细中【产品】不能为空";
        }
        if (static::is_null_array(array_column($result['array'], 'unit'))) {
            return "发货明细中【单位】不能为空";
        }
//        if (static::is_null_array(array_column($result['array'], 'warehouseId'))) {
//            return "发货明细中【发货仓库】不能为空";
//        }
//        if (static::is_null_array(array_column($result['array'], 'price'))) {
//            return "发货明细中【单价】不能为空";
//        }
        if (static::is_null_array(array_column($result['array'], 'num'), false)) {
            return "发货明细中【数量】不能为空，且必须大于0";
        }

        //判断客户信用额度
        $setting = optionSettingClass::getSetting('销售');
        if (isset($setting['creditCtrl']['option']) && $setting['creditCtrl']['option'] && $setting['creditCtrl']['way'] == '保存') {
            $kehu    = query('kehu', "khid = '$info[khid]'");
            $balance = saleOrderClass::clientBalance($info['khid']);
            if ($kehu['credits'] < $balance) {
                return '客户信用额度不足';
            }
        }

        return true;
    }

    /**
     * 提交
     */
    public function commitEdit()
    {
        $id    = $this->post('id');
        $info  = query(self::table, " id = '$id'");
        $field = [
            'state' => '待确认',
        ];
        //提交前数据验证
        $json['warn'] = $this->dataValidation($info);
        if ($json['warn'] !== true) {
            goto resp;
        }

        if (!$this->power(self::page, 'edit')) {
            $json['warn'] = '你还没有编辑权限';
        } elseif (empty($info['id'])) {
            $json['warn'] = '未找到此销售发货单';
        } elseif ($info['state'] != '草稿') {
            $json['warn'] = '此销售发货单不是草稿状态，不能提交';
        } else {
            $this->startTrans();
            $json['warn'] = update(array(
                "table" => self::table,
                "field" => $field,
                "where" => "id = '$info[id]'",
            ));
            $all          = find(array('table' => self::tableMx, 'where' => "orderId = '$info[id]' order by time asc"));
            foreach ($all['array'] as $array) {
                $material = query('material', "id = '$array[materialId]'");
                if ($material['OQC']) {
                    //$allTypeId   = machiningInOrderClass::getAllParentTypeId($material['typeId']);
                    $allTypeId = explode('/', $material['typePath']);
                    //查找质检方案
                    $quarantine = query(
                        'quarantine',
                        "application like '%发货检验%' and coid = '" . self::$coid . "' and state = '启用' and startDate<= date_format(now(),'%Y-%m-%d') and endDate>date_format(now(),'%Y-%m-%d')  and if(materialId,materialId = '$material[id]',materialTypeId in ('" . implode("','", $allTypeId) . "')) order by materialId desc,list desc,time desc"
                    );
                    if (empty($quarantine['id'])) {
                        $json['warn'] = "未找到物料【{$material['name']}】的质检方案";
                        break;
                    }
                }
            }
            if (in_array($json['warn'], ["新增成功", "更新成功"])) {
                // 单据是否需要复核
                if (!in_array(self::page, self::$publicSettings['documentReview'])) {
                    $resultJson = $this->confirmEdit();
                    $json       = json_decode($resultJson, true);
                    if ($json['warn'] != 2) {
                        $this->rollback();
                        goto resp;
                    }
                } else {
                    self::insertOperateLog($info['id'], self::title, "提交");
                }
                $this->commitTrans();
                //添加日志
                $type = mb_substr($json['warn'], 0, 2, 'utf-8');
                $text = $this->control['name'] . $type . "了" . self::title . "id：【" . $id . '】';
                $this->logText(["type" => self::page, "text" => $text]);
                //返回
                $_SESSION['warn'] = $json['warn'];
                $json['warn']     = 2;
                $json['href']     = root . "control/adIndex.html?cla=" . $this->get('cla') . "&fun=detail&id=" . $info['id'];
            } else {
                $this->rollback();
            }
        }
        resp:
        return json_encode($json, 320);
    }

    // 操作中转
    public function operate()
    {
        $act = $this->post('act') ?? '';
        $pwd = $word = false; // 需要密码
        switch ($act) {
            case 'confirm':
                $act      = 'confirm';
                $msg      = '确认';
                $title    = '是否已确认此销售发货单，确认后将不可修改？';
                $function = 'confirmEdit';
                $form     = self::page . 'OperateForm, detailForm';
                $power    = '';
                $word     = true;
                break;
            case 'commit':
                $act      = 'edit';
                $msg      = '编辑';
                $title    = '是否确认提交此销售发货单？';
                $function = 'commitEdit';
                $form     = self::page . 'OperateForm, detailForm';
                $power    = '';
                $word     = true;
                break;
            case 'cancel':
                $act      = 'cancel';
                $msg      = '撤回';
                $title    = '是否确认撤回此销售发货单？';
                $function = 'cancelEdit';
                $form     = self::page . 'OperateForm, detailForm';
                $power    = '';
                $word     = true;
                break;
            default:
                $act      = 'del';
                $msg      = '删除' . self::title;
                $title    = self::page . 'Delete';
                $function = 'delete';
                $form     = self::page . 'OperateForm, listForm, quoForm';
                $power    = '';
                break;
        }
        if (!$this->power(self::page, $act)) {
            $_SESSION['warn'] = '你还没有' . $msg . '的权限';
            $json             = [
                'warn' => 2,
                'href' => root . 'control/adIndex.html?cla=' . $this->get('cla') . '&fun=home',
            ];
        } else {
            return $this->confirm([
                'title'    => $title,
                'function' => $function,
                'form'     => $form,
                'power'    => $power,
                'pwd'      => $pwd,
                'word'     => $word,
            ]);
        }

        resp:
        return json_encode($json, 320);
    }

    /**
     * 删除
     *
     * @return bool|string
     */
    public function delete()
    {
        $array = $this->post('listId') ?? $this->post('id');
        if (!$this->power(self::page, "del")) {
            $json['warn'] = "你没有删除的权限";
        } elseif (empty($array)) {
            $json['warn'] = "你一个数据都没有选择呢";
        } else {
            $text  = "";
            $x     = 0;
            $array = is_array($array) ? $array : [$array];
            foreach ($array as $id) {
                $info = query(self::table, " id = '$id' ");
                if ($info['state'] == '已确认') {
                    $warn = "删除失败：已确认，暂不能删除";
                } else {
                    //最后删除基本参数
                    fileClass::deleteFile($id);
                    delete(array("table" => self::table, "where" => " id = '$id' "));
                    delete(array("table" => self::tableMx, "where" => " orderId = '$id' "));
                    delete(array("table" => "auditing_follow", "where" => " typeId = '$id' "));
                    //返回
                    $warn = "删除成功";
                    $x++;
                }
                $text .= self::title . "ID【" . $info['id'] . "】" . $warn . "。<br>";
            }
            //添加日志
            $text = $this->control['name'] . "删除了" . self::title . "，结果如下：<br>" . $text;
            $this->logText(array("type" => self::page, "text" => $text));
            $_SESSION['warn'] = "删除了" . $x . "个" . self::title . "，详情见系统日志。";
            $json['warn']     = 2;
            $json['href']     = root . "control/adIndex.html?cla=" . $this->get('cla') . "&fun=home";
        }
        return json_encode($json, 320);
    }

    /**
     * 批量打印
     *
     * @return string
     */
    public function batchPrint(): string
    {
        $lists = $this->get('lists');
        if (empty($lists)) {
            exit('参数错误');
        } else {
            $listId = base64_decode($lists, true);
            $array  = explode(',', $listId);
            $data   = [];
            foreach ($array as $id) {
                $data[] = static::printPage($id, false);
            }
            return (new printTemplateClass())->start(self::page, $data);
        }
    }

    /**
     * 打印
     *
     * @return string
     */
    public function printPage($param = '', $isReturn = true): string|array
    {

        $id                  = $param ?: $this->get('id');
        $info                = query(self::table, " id = '$id'");
        $department_name                = query('department', " id = '$info[departmentId]'");
        $logistics_car                = query('logistics_car', " id = '$info[car_id]'");
        $info['companyName'] = query('company', "coid = '" . self::$coid . "'")['name']; //公司名称//公司名称
        $info['title']       = self::title;
        $info['printTime']   = $this->time;
        $info['stid']        = self::findStaff($info['stid']);
        $info['checkId']     = '';
        $info['khName']      = query('kehu', "khid = '$info[khid]'")['companyName'];
        $info['contractId']  = query('sales_contract', "id = '$info[contractId]'")['name'];
        $info['qr']          = "source=" . self::table . "&id=" . $info['id'];
        $info['tags']        = orderTagClass::getTagName($info['id']);
        $info['car_id']        = $logistics_car['shortName'].$logistics_car['carNo'];
        $info['delivery_name']        = self::findStaff($info['delivery_name']);
        $info['delivery_manage']        = self::findStaff($info['delivery_manage']);
        $info['department_name']        = $department_name['name'];
        $detail              = find(['table' => self::tableMx, 'where' => "orderId = '$info[id]' order by list asc"])['array'];
        $items               = [];
        $x                   = 1;
        $totalNum            = 0;
        $totalMoney          = 0;
        foreach ($detail as $array) {
            $material    = query('material', "id = '$array[materialId]'");
            $saleOrderMx = query("sale_order_detail", "id = '$array[saleOrderMxId]'");
            $saleOrder   = query('sale_order', "id = '$saleOrderMx[orderId]'");
            $items[]     = [
                'no'               => $x,
                'materialCode'     => $material['code'],
                'typeName'     => $material['typeName'],
                'materialName'     => $material['name'],
                'warehouse'        => warehouseClass::getName($array['warehouseId']),
                'price'            => self::lostZero($array['price'], 'pricePrecision'),
                'rate'             => ($array['rate'] * 100) . '%',
                'taxMoney'         => self::lostZero($array['taxMoney'], 'amount'),
                'khMaterialName'   => $array['khMaterialName'],
                'khMaterialCode'   => $array['khMaterialCode'],
                'moneyWithOutTax'  => self::lostZero($array['money'] - $array['taxMoney'], 'amount'),
                'money'            => self::lostZero($array['money'], 'amount'),
                'model'            => $material['model'],
                'unit'             => $array['unit'],
                'num'              => self::lostZero($array['num'], 'numPrecision'),
                'packing_quantity' => $array['packing_quantity'],
                'box_capacity'     => $array['box_capacity'],
                'remainder'        => $array['remainder'],
                'sourceNo'         => $saleOrder['orderNo'],
                'text'         => $array['text'],

            ];
            $x++;
            $totalNum   += $array['num'];
            $totalMoney += $array['money'];
        }
        $append         = [
            'no'    => '合计',
            'num'   => self::lostZero($totalNum, 'amount'),
            'money' => self::lostZero($totalMoney, 'amount'),
        ];
        $items[]        = $append;
        $info['detail'] = $items;
        if ($isReturn) {
            return (new printTemplateClass())->start(self::page, $info, '销售发货单');
        } else {
            return $info;
        }
    }

    /**
     * 打印产品标识卡
     *
     * @return string
     */
    public function printCard(): string
    {
        $idArr = $this->get('body') ? explode(';', $this->get('body')) : [];
        $data  = [];
        foreach ($idArr as $id) {
            if (empty($id)) {
                continue;
            }
            $detail = find(['table' => self::tableMx, 'where' => "orderId = '$id' order by list asc"])['array'];
            foreach ($detail as $array) {
                $material = query('material', "id = '$array[materialId]'");
                $data[]   = [
                    'materialCode'     => $material['code'],
                    'materialName'     => $material['name'],
                    'khMaterialName'   => $array['khMaterialName'],
                    'khMaterialCode'   => $array['khMaterialCode'],
                    'model'            => $material['model'],
                    'batch'            => $array['batch'],
                    'packing_quantity' => self::lostZero($array['packing_quantity'], 'numPrecision'),
                    'box_capacity'     => self::lostZero($array['box_capacity'], 'numPrecision'),
                    'remainder'        => self::lostZero($array['remainder'], 'numPrecision'),
                    'num'              => self::lostZero($array['num'], 'numPrecision'),
                    'manufacturer'     => 'manufacturer',
                ];
            }
        }
        return (new printTemplateClass())->start(self::page, $data, '产品标识卡');
    }
}
